diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index cad899ef3..8bff66f33 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable @@ -70,7 +70,7 @@ jobs: fetch-depth: 0 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/build-cli-docker.yml b/.github/workflows/build-cli-docker.yml index 5cee3899b..19c5da2ca 100644 --- a/.github/workflows/build-cli-docker.yml +++ b/.github/workflows/build-cli-docker.yml @@ -3,13 +3,12 @@ name: Build bws Docker image on: push: - paths: - - "crates/bws/**" + branches: + - "main" + - "rc" + - "hotfix-rc" workflow_dispatch: pull_request: - paths: - - ".github/workflows/build-cli-docker.yml" - - "crates/bws/**" env: _AZ_REGISTRY: bitwardenprod.azurecr.io @@ -106,7 +105,7 @@ jobs: platforms: | linux/amd64, linux/arm64/v8 - push: ${{ env.is_publish_branch }} + push: true tags: ${{ steps.tag-list.outputs.tags }} secrets: | "GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index e60928807..ad3386ddf 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -61,7 +61,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} @@ -151,7 +151,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} @@ -260,7 +260,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} @@ -411,7 +411,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml index 58bbd5dfa..b08b37160 100644 --- a/.github/workflows/build-dotnet.yml +++ b/.github/workflows/build-dotnet.yml @@ -4,6 +4,10 @@ on: push: branches: - main + - rc + - hotfix-rc + pull_request: + workflow_dispatch: jobs: generate_schemas: @@ -12,12 +16,31 @@ jobs: build_rust: uses: ./.github/workflows/build-rust-cross-platform.yml + version: + name: Get version + runs-on: ubuntu-22.04 + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Install xmllint + run: sudo apt-get install -y libxml2-utils + + - name: Get version + id: version + run: | + VERSION=$(xmllint --xpath 'string(/Project/PropertyGroup/Version)' languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj) + echo "version=$VERSION" >> $GITHUB_OUTPUT + build_dotnet: name: Build .NET runs-on: ubuntu-22.04 needs: - generate_schemas - build_rust + - version steps: - name: Checkout Repository @@ -58,21 +81,19 @@ jobs: name: libbitwarden_c_files-x86_64-pc-windows-msvc path: languages/csharp/Bitwarden.Sdk/windows-x64 - - name: Build .NET 6 Project + - name: Build .NET Project working-directory: languages/csharp/Bitwarden.Sdk run: | dotnet restore dotnet build --configuration Release - name: Pack NuGet Package - env: - VERSION: 0.0.1 - run: dotnet pack --configuration Release -p:PackageID=Bitwarden.Sdk -p:Version=${VERSION} --output ./nuget-output /nologo /v:n + run: dotnet pack --configuration Release --output ./nuget-output /nologo /v:n working-directory: languages/csharp/Bitwarden.Sdk - name: Upload NuGet package uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: - name: Bitwarden.Sdk.0.0.1.nupkg + name: Bitwarden.Sdk.${{ needs.version.outputs.version }}.nupkg path: | ./languages/csharp/Bitwarden.Sdk/nuget-output/*.nupkg diff --git a/.github/workflows/build-napi.yml b/.github/workflows/build-napi.yml index aa1cfdd16..3737d3767 100644 --- a/.github/workflows/build-napi.yml +++ b/.github/workflows/build-napi.yml @@ -61,7 +61,7 @@ jobs: cache-dependency-path: crates/bitwarden-napi/package-lock.json - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} diff --git a/.github/workflows/build-python-wheels.yml b/.github/workflows/build-python-wheels.yml index 7ffc2f24f..1c0459dd6 100644 --- a/.github/workflows/build-python-wheels.yml +++ b/.github/workflows/build-python-wheels.yml @@ -71,7 +71,7 @@ jobs: node-version: 18 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} diff --git a/.github/workflows/build-rust-crates.yml b/.github/workflows/build-rust-crates.yml index 5d45b6f95..dc6db5c91 100644 --- a/.github/workflows/build-rust-crates.yml +++ b/.github/workflows/build-rust-crates.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} @@ -69,7 +69,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: ${{ matrix.settings.target }} diff --git a/.github/workflows/build-rust-cross-platform.yml b/.github/workflows/build-rust-cross-platform.yml index 0db2d0cde..cacacf0e0 100644 --- a/.github/workflows/build-rust-cross-platform.yml +++ b/.github/workflows/build-rust-cross-platform.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/direct-minimal-versions.yml b/.github/workflows/direct-minimal-versions.yml index 8db6e3c2e..084ed3cff 100644 --- a/.github/workflows/direct-minimal-versions.yml +++ b/.github/workflows/direct-minimal-versions.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: nightly targets: ${{ matrix.settings.target }} diff --git a/.github/workflows/generate_schemas.yml b/.github/workflows/generate_schemas.yml index bd206b21e..5c62cee01 100644 --- a/.github/workflows/generate_schemas.yml +++ b/.github/workflows/generate_schemas.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 771b368f5..8767cba89 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/memory-testing.yml b/.github/workflows/memory-testing.yml index af5ef6b7c..1723644f2 100644 --- a/.github/workflows/memory-testing.yml +++ b/.github/workflows/memory-testing.yml @@ -30,7 +30,7 @@ jobs: sudo apt -y install gdb - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/minimum-rust-version.yml b/.github/workflows/minimum-rust-version.yml index d3eccf653..6213fed32 100644 --- a/.github/workflows/minimum-rust-version.yml +++ b/.github/workflows/minimum-rust-version.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: # Important: When updating this, make sure to update the Readme file # and also the `rust-version` field in all the `Cargo.toml`. diff --git a/.github/workflows/publish-dotnet.yml b/.github/workflows/publish-dotnet.yml index 2f9304903..73f930c1a 100644 --- a/.github/workflows/publish-dotnet.yml +++ b/.github/workflows/publish-dotnet.yml @@ -1,70 +1,78 @@ -name: Deploy NuGet Package +name: Publish .NET NuGet +run-name: Publish .NET NuGet Package ${{ inputs.release_type }} on: workflow_dispatch: inputs: - version_number: - description: "New Version" + release_type: + description: "Release Options" required: true + default: "Release" + type: choice + options: + - Release + - Dry Run -jobs: - generate_schemas: - uses: ./.github/workflows/generate_schemas.yml - - build_rust: - uses: ./.github/workflows/build-rust-cross-platform.yml +env: + _KEY_VAULT: "bitwarden-ci" - deploy: - name: Deploy +jobs: + validate: + name: Setup runs-on: ubuntu-22.04 - needs: - - generate_schemas - - build_rust - + outputs: + version: ${{ steps.version.outputs.version }} steps: - - name: Checkout Repository + - name: Checkout repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Download C# schemas artifact - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 - with: - name: schemas.cs - path: languages/csharp/Bitwarden.Sdk + - name: Branch check + if: ${{ inputs.release_type != 'Dry Run' }} + run: | + if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then + echo "===================================" + echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches" + echo "===================================" + exit 1 + fi - - name: Set up .NET Core - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 - with: - global-json-file: languages/csharp/global.json + - name: Install xmllint + run: sudo apt-get install -y libxml2-utils - - name: Download x86_64-apple-darwin files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 - with: - name: libbitwarden_c_files-x86_64-apple-darwin - path: languages/csharp/Bitwarden.Sdk/macos-x64 + - name: Get version + id: version + run: | + VERSION=$(xmllint --xpath 'string(/Project/PropertyGroup/Version)' languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj) + echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Download aarch64-apple-darwin files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + deploy: + name: Deploy + runs-on: ubuntu-22.04 + needs: validate + steps: + - name: Download NuGet package + uses: bitwarden/gh-actions/download-artifacts@main with: - name: libbitwarden_c_files-aarch64-apple-darwin - path: languages/csharp/Bitwarden.Sdk/macos-arm64 + workflow: build-dotnet.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: Bitwarden.Sdk.${{ needs.validate.outputs.version }}.nupkg + path: ./nuget-output - - name: Download x86_64-unknown-linux-gnu files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + - name: Login to Azure - Prod Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: - name: libbitwarden_c_files-x86_64-unknown-linux-gnu - path: languages/csharp/Bitwarden.Sdk/linux-x64 + creds: ${{ secrets.AZURE_CI_SERVICE_PRINCIPAL }} - - name: Download x86_64-pc-windows-msvc files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main with: - name: libbitwarden_c_files-x86_64-pc-windows-msvc - path: languages/csharp/Bitwarden.Sdk/windows-x64 - - - name: Pack NuGet Package - env: - VERSION: ${{ github.event.inputs.version_number }} - run: dotnet pack --configuration Release -p:PackageID=Bitwarden.Sdk -p:Version=${VERSION} --output ./nuget-output /nologo /v:n - working-directory: languages/csharp/Bitwarden.Sdk + keyvault: ${{ env._KEY_VAULT }} + secrets: "nuget-api-key" - name: Publish NuGet Package - run: dotnet nuget push ./languages/csharp/Bitwarden.Sdk/nuget-output/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json + if: ${{ inputs.release_type != 'Dry Run' }} + env: + NUGET_API_KEY: ${{ steps.retrieve-secrets.outputs.nuget-api-key }} + run: dotnet nuget push ./nuget-output/*.nupkg -k ${{ env.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json diff --git a/.github/workflows/publish-php.yml b/.github/workflows/publish-php.yml index 00b34c23f..2b716c893 100644 --- a/.github/workflows/publish-php.yml +++ b/.github/workflows/publish-php.yml @@ -1,20 +1,50 @@ name: Publish PHP SDK +run-name: Publish PHP SDK ${{ inputs.release_type }} on: - push: - branches: - - main + workflow_dispatch: + inputs: + release_type: + description: "Release Options" + required: true + default: "Release" + type: choice + options: + - Release + - Dry Run + +env: + _KEY_VAULT: "bitwarden-ci" jobs: - build_rust: - uses: ./.github/workflows/build-rust-cross-platform.yml + validate: + name: Setup + runs-on: ubuntu-22.04 + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - setup_php: + - name: Branch check + if: ${{ inputs.release_type != 'Dry Run' }} + run: | + if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then + echo "===================================" + echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches" + echo "===================================" + exit 1 + fi + + - name: Get version + id: version + run: | + VERSION=$(cat languages/php/composer.json | grep -Eo '"version": "[0-9]+\.[0-9]+\.[0-9]+"' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + setup-php: name: Setup PHP runs-on: ubuntu-22.04 - needs: - - build_rust - steps: - name: Checkout Repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -28,48 +58,212 @@ jobs: - name: Composer check run: | + composer update composer install composer validate working-directory: languages/php/ - - name: Download x86_64-apple-darwin files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + repo-sync: + name: Push changed files to SDK PHP repo + runs-on: ubuntu-22.04 + needs: + - validate + - setup-php + env: + _BOT_EMAIL: 106330231+bitwarden-devops-bot@users.noreply.github.com + _BOT_NAME: bitwarden-devops-bot + _PKG_VERSION: ${{ needs.validate.outputs.version }} + steps: + - name: Checkout SDK repo + uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4.0.0 with: - name: libbitwarden_c_files-x86_64-apple-darwin - path: temp/macos-x64 + path: sdk - - name: Download aarch64-apple-darwin files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + - name: Login to Azure - Prod Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: - name: libbitwarden_c_files-aarch64-apple-darwin - path: temp/macos-arm64 + creds: ${{ secrets.AZURE_CI_SERVICE_PRINCIPAL }} - - name: Download x86_64-unknown-linux-gnu files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@2e9a183f0543d25868fbb784dfe205fea6ff09a5 with: - name: libbitwarden_c_files-x86_64-unknown-linux-gnu - path: temp/linux-x64 + keyvault: ${{ env._KEY_VAULT }} + secrets: "github-pat-bitwarden-devops-bot-repo-scope" - - name: Download x86_64-pc-windows-msvc files - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + - name: Checkout SDK-PHP repo + uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4.0.0 with: - name: libbitwarden_c_files-x86_64-pc-windows-msvc - path: temp/windows-x64 + repository: bitwarden/sm-sdk-php + path: sm-sdk-php + ref: main + token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} - - name: Copy lib files + - name: Setup Git + working-directory: sm-sdk-php run: | - mkdir -p languages/php/src/lib/macos-arm64 - mkdir -p languages/php/src/lib/linux-x64 - mkdir -p languages/php/src/lib/macos-x64 - mkdir -p languages/php/src/lib/windows-x64 + git config --local user.email "${{ env._BOT_EMAIL }}" + git config --local user.name "${{ env._BOT_NAME }}" - platforms=("macos-arm64" "linux-x64" "macos-x64" "windows-x64") - files=("libbitwarden_c.dylib" "libbitwarden_c.so" "libbitwarden_c.dylib" "bitwarden_c.dll") + - name: Update files + run: | + # Copy files to local sm-sdk-php repo path + cp --verbose -rf sdk/languages/php/. sm-sdk-php + + - name: Replace repo name + working-directory: sm-sdk-php + run: | + find . -name '*' -exec \ + sed -i -e 's/github.com\/bitwarden\/sdk\/languages\/php/github.com\/bitwarden\/sm-sdk-php/g' {} \; - for ((i=0; i<${#platforms[@]}; i++)); do - cp "temp/${platforms[$i]}/${files[$i]}" "languages/php/src/lib/${platforms[$i]}/${files[$i]}" - done + find . -name '*' -exec \ + sed -i -e 's/github.com\/bitwarden\/sdk/github.com\/bitwarden\/sm-sdk-php/g' {} \; + + - name: Push changes + working-directory: sm-sdk-php + run: | + git add . + git commit -m "Update Go SDK to ${{ github.sha }}" + + if [[ "${{ inputs.release_type }}" == "Dry Run" ]]; then + echo "===================================" + echo "[!] Dry Run - Skipping push" + echo "===================================" + git ls-files -m + exit 0 + else + git push origin main + fi + + - name: Create release tag on SDK Go repo + if: ${{ inputs.release_type != 'Dry Run' }} + working-directory: sm-sdk-php + run: | + # Check if tag exists, set output then exit 0 if true. + if git log v${{ env._PKG_VERSION }} >/dev/null 2>&1; then + echo "===================================" + echo "[!] Tag v${{ env._PKG_VERSION }} already exists" + echo "===================================" + exit 1 + fi + + git tag v${{ env._PKG_VERSION }} + git push origin v${{ env._PKG_VERSION }} + + github-release: + name: GitHub Release + runs-on: ubuntu-22.04 + needs: + - setup-php + - repo-sync + - validate + env: + _PKG_VERSION: ${{ needs.validate.outputs.version }} + steps: + - name: Login to Azure - Prod Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + with: + creds: ${{ secrets.AZURE_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@2e9a183f0543d25868fbb784dfe205fea6ff09a5 + with: + keyvault: ${{ env._KEY_VAULT }} + secrets: "github-pat-bitwarden-devops-bot-repo-scope" + + - name: Download x86_64-apple-darwin artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-x86_64-apple-darwin + skip_unpack: true + + - name: Download aarch64-apple-darwin artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-aarch64-apple-darwin + skip_unpack: true + + - name: Download x86_64-unknown-linux-gnu artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-x86_64-unknown-linux-gnu + skip_unpack: true + + - name: Download x86_64-pc-windows-msvc artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-x86_64-pc-windows-msvc + skip_unpack: true + + - name: Rename build artifacts + run: | + mv libbitwarden_c_files-x86_64-apple-darwin.zip libbitwarden_c_files-x86_64-apple-darwin-$_PKG_VERSION.zip + mv libbitwarden_c_files-aarch64-apple-darwin.zip libbitwarden_c_files-aarch64-apple-darwin-$_PKG_VERSION.zip + mv libbitwarden_c_files-x86_64-unknown-linux-gnu.zip libbitwarden_c_files-x86_64-unknown-linux-gnu-$_PKG_VERSION.zip + mv libbitwarden_c_files-x86_64-pc-windows-msvc.zip libbitwarden_c_files-x86_64-pc-windows-msvc-$_PKG_VERSION.zip + + - name: Create release + if: ${{ inputs.release_type != 'Dry Run' }} + uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0 + with: + tag: v${{ env._PKG_VERSION }} + name: v${{ env._PKG_VERSION }} + body: "" + token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} + draft: true + repo: sm-sdk-php + owner: bitwarden + artifacts: "libbitwarden_c_files-x86_64-apple-darwin-${{ env._PKG_VERSION }}.zip, + libbitwarden_c_files-aarch64-apple-darwin-${{ env._PKG_VERSION }}.zip, + libbitwarden_c_files-x86_64-unknown-linux-gnu-${{ env._PKG_VERSION }}.zip, + libbitwarden_c_files-x86_64-pc-windows-msvc-${{ env._PKG_VERSION }}.zip" + + packagist-publish: + name: Publish to Packagist + runs-on: ubuntu-22.04 + needs: + - validate + - setup-php + - repo-sync + - github-release + steps: + - name: Login to Azure - Prod Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + with: + creds: ${{ secrets.AZURE_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@2e9a183f0543d25868fbb784dfe205fea6ff09a5 + with: + keyvault: ${{ env._KEY_VAULT }} + secrets: "github-pat-bitwarden-devops-bot-repo-scope, + packagist-key" + + - name: Checkout SDK-PHP repo + uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4.0.0 + with: + repository: bitwarden/sm-sdk-php + path: sm-sdk-php + ref: main + token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} - name: Publish version - run: curl -XPOST -H'content-type:application/json' 'https://packagist.org/api/update-package?username=malirobot&apiToken=${{secrets.PACKAGIST_KEY}}' -d'{"repository":{"url":"https://packagist.org/packages/bitwarden/sdk"}}' - working-directory: languages/php/ + if: ${{ inputs.release_type != 'Dry Run' }} + env: + PACKAGIST_KEY: ${{ steps.retrieve-secrets.outputs.packagist-key }} + run: curl -XPOST -H'content-type:application/json' 'https://packagist.org/api/update-package?username=bitwarden&apiToken=${{ env.PACKAGIST_KEY }}' -d'{"repository":{"url":"https://packagist.org/packages/bitwarden/sdk-secrets"}}' + working-directory: sm-sdk-php diff --git a/.github/workflows/publish-rust-crates.yml b/.github/workflows/publish-rust-crates.yml index e3b2a1626..f6a52d5d1 100644 --- a/.github/workflows/publish-rust-crates.yml +++ b/.github/workflows/publish-rust-crates.yml @@ -137,7 +137,7 @@ jobs: secrets: "cratesio-api-token" - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index daf6dd622..3b42c3238 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -142,7 +142,7 @@ jobs: secrets: "cratesio-api-token" - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index c7a198056..8ea03e928 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -58,15 +58,6 @@ jobs: with: path: sdk - - name: Download artifacts - uses: bitwarden/gh-actions/download-artifacts@main - with: - workflow: generate_schemas.yml - path: sdk/languages/go/bitwarden_sdk_secrets/lib - workflow_conclusion: success - branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} - artifacts: schemas.go - - name: Login to Azure - Prod Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: @@ -74,7 +65,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@4f37134d838f21609c38cb56694d8605f176704c + uses: bitwarden/gh-actions/get-keyvault-secrets@main with: keyvault: ${{ env._KEY_VAULT }} secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -98,6 +89,15 @@ jobs: # Copy files to local sm-sdk-go repo path cp --verbose -rf sdk/languages/go/. sm-sdk-go + - name: Download artifacts + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: generate_schemas.yml + path: sm-sdk-go + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: schemas.go + - name: Replace repo name working-directory: sm-sdk-go run: | @@ -151,7 +151,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@4f37134d838f21609c38cb56694d8605f176704c + uses: bitwarden/gh-actions/get-keyvault-secrets@main with: keyvault: ${{ env._KEY_VAULT }} secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -194,10 +194,16 @@ jobs: - name: Rename build artifacts run: | - mv libbitwarden_c_files-x86_64-apple-darwin.zip libbitwarden_c_files-x86_64-apple-darwin-$_PKG_VERSION.zip - mv libbitwarden_c_files-aarch64-apple-darwin.zip libbitwarden_c_files-aarch64-apple-darwin-$_PKG_VERSION.zip - mv libbitwarden_c_files-x86_64-unknown-linux-gnu.zip libbitwarden_c_files-x86_64-unknown-linux-gnu-$_PKG_VERSION.zip - mv libbitwarden_c_files-x86_64-pc-windows-msvc.zip libbitwarden_c_files-x86_64-pc-windows-msvc-$_PKG_VERSION.zip + artifacts=("x86_64-apple-darwin" "aarch64-apple-darwin" "x86_64-unknown-linux-gnu" "x86_64-pc-windows-msvc") # aarch64-unknown-linux-gnu) + for value in "${artifacts[@]}" + do + unzip libbitwarden_c_files-$value.zip -d libbitwarden_c_files-$value + cd libbitwarden_c_files-$value + zip -Rj ../libbitwarden_c_files-$value-$_PKG_VERSION.zip 'libbitwarden_c.*' + cd .. + done + + ls ./libbitwarden_c_files-x86_64-apple-darwin-$_PKG_VERSION -lRa - name: Create release if: ${{ inputs.release_type != 'Dry Run' }} diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 8408dd1d7..06904ee3b 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable components: llvm-tools @@ -87,7 +87,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable targets: wasm32-unknown-unknown diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml new file mode 100644 index 000000000..dea39cfd6 --- /dev/null +++ b/.github/workflows/scan.yml @@ -0,0 +1,77 @@ +name: Scan + +on: + workflow_dispatch: + push: + branches: + - "main" + - "rc" + - "hotfix-rc" + pull_request_target: + types: [opened, synchronize] + +jobs: + check-run: + name: Check PR run + uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main + + sast: + name: SAST scan + runs-on: ubuntu-22.04 + needs: check-run + permissions: + contents: read + pull-requests: write + security-events: write + + steps: + - name: Check out repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Scan with Checkmarx + uses: checkmarx/ast-github-action@749fec53e0db0f6404a97e2e0807c3e80e3583a7 #2.0.23 + env: + INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}" + with: + project_name: ${{ github.repository }} + cx_tenant: ${{ secrets.CHECKMARX_TENANT }} + base_uri: https://ast.checkmarx.net/ + cx_client_id: ${{ secrets.CHECKMARX_CLIENT_ID }} + cx_client_secret: ${{ secrets.CHECKMARX_SECRET }} + additional_params: | + --report-format sarif \ + --filter "state=TO_VERIFY;PROPOSED_NOT_EXPLOITABLE;CONFIRMED;URGENT" \ + --output-path . ${{ env.INCREMENTAL }} + + - name: Upload Checkmarx results to GitHub + uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + with: + sarif_file: cx_result.sarif + + quality: + name: Quality scan + runs-on: ubuntu-22.04 + needs: check-run + permissions: + contents: read + pull-requests: write + + steps: + - name: Check out repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: Scan with SonarCloud + uses: sonarsource/sonarcloud-github-action@49e6cd3b187936a73b8280d59ffd9da69df63ec9 # v2.1.1 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: > + -Dsonar.organization=${{ github.repository_owner }} + -Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }} + -Dsonar.exclusions=languages/** diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 4143af953..8b4c33ee3 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -16,6 +16,8 @@ on: - python-sdk - ruby-sdk - go-sdk + - dotnet-sdk + - php-sdk version_number: description: "New version (example: '2024.1.0')" required: true @@ -30,7 +32,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install rust - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 # stable with: toolchain: stable @@ -127,6 +129,17 @@ jobs: if: ${{ inputs.project == 'go-sdk' }} run: sed -i 's/[0-9]\.[0-9]\.[0-9]/${{ inputs.version_number }}/' ./languages/go/.version + ### dotnet sdk + - name: Bump dotnet-sdk Version + if: ${{ inputs.project == 'dotnet-sdk' }} + run: sed -i 's/[0-9]\.[0-9]\.[0-9]<\/Version>/${{ inputs.version_number }}<\/Version>/' languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj + + ### php sdk + - name: Bump php-sdk Version + if: ${{ inputs.project == 'php-sdk' }} + run: | + sed -i 's/"version": "[0-9]\.[0-9]\.[0-9]"/"version": "${{ inputs.version_number }}"/' ./languages/php/composer.json + ############################ # VERSION BUMP SECTION END # ############################ diff --git a/Cargo.lock b/Cargo.lock index 1a7816b21..b3538ccab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -64,9 +64,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -112,15 +112,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" [[package]] name = "argon2" @@ -158,7 +158,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -212,13 +212,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -256,9 +256,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-toml" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" dependencies = [ "serde", ] @@ -323,9 +323,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitwarden" @@ -561,9 +561,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.0" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bw" @@ -612,15 +612,15 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.8.10", + "toml 0.8.12", "uuid", ] [[package]] name = "bytemuck" -version = "1.14.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -683,12 +683,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cesu8" @@ -704,9 +701,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -714,7 +711,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -730,9 +727,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", "clap_derive", @@ -740,9 +737,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -761,14 +758,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -791,9 +788,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -992,7 +989,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossterm_winapi", "libc", "parking_lot", @@ -1041,12 +1038,12 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" dependencies = [ "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -1188,9 +1185,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "either" @@ -1225,9 +1222,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", @@ -1389,7 +1386,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -1484,36 +1481,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.11", - "indexmap 2.2.3", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http 1.0.0", - "indexmap 2.2.3", + "http", + "indexmap 2.2.5", "slab", "tokio", "tokio-util", @@ -1538,11 +1516,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1579,37 +1563,15 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.11", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.0" @@ -1617,19 +1579,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http", ] [[package]] name = "http-body-util" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", - "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "futures-core", + "http", + "http-body", "pin-project-lite", ] @@ -1653,60 +1615,40 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.2", - "http 1.0.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", + "smallvec", "tokio", "want", ] [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 0.2.11", - "hyper 0.14.28", + "http", + "hyper", + "hyper-util", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", ] [[package]] @@ -1716,13 +1658,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", + "futures-channel", "futures-util", - "http 1.0.0", - "http-body 1.0.0", - "hyper 1.1.0", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -1783,9 +1729,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1881,9 +1827,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1905,12 +1851,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.4", ] [[package]] @@ -1925,7 +1871,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", "redox_syscall", ] @@ -1957,9 +1903,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" @@ -2033,11 +1979,11 @@ dependencies = [ [[package]] name = "napi" -version = "2.15.2" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd92040344a83763379b122f4e714932ccaa700439e647f1e90481dd1999cb" +checksum = "54a63d0570e4c3e0daf7a8d380563610e159f538e20448d6c911337246f40e84" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "ctor", "napi-derive", "napi-sys", @@ -2053,23 +1999,23 @@ checksum = "2f9130fccc5f763cf2069b34a089a18f0d0883c66aceb81f2fad541a3d823c43" [[package]] name = "napi-derive" -version = "2.15.2" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef56a31cf81a1d183aa991c85128e6f27c312b72227e506b1854068c2ac7d53" +checksum = "05bb7c37e3c1dda9312fdbe4a9fc7507fca72288ba154ec093e2d49114e727ce" dependencies = [ "cfg-if", "convert_case", "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] name = "napi-derive-backend" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b8f403a37007cad225039fc0323b961bb40d697eea744140920ebb689ff1d" +checksum = "f785a8b8d7b83e925f5aa6d2ae3c159d17fe137ac368dc185bef410e7acdaeb4" dependencies = [ "convert_case", "once_cell", @@ -2077,7 +2023,7 @@ dependencies = [ "quote", "regex", "semver", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -2331,6 +2277,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2383,13 +2349,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ "base64", - "indexmap 2.2.3", + "indexmap 2.2.5", "line-wrap", "quick-xml", "serde", "time", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "powerfmt" version = "0.2.0" @@ -2404,24 +2376,25 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89dc7a5850d0e983be1ec2a463a171d20990487c3cfcd68b5363f1ee3d6fe0" +checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", "parking_lot", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -2455,9 +2428,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07426f0d8fe5a601f26293f300afd1a7b1ed5e78b2a705870c5f30893c5163be" +checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7" dependencies = [ "once_cell", "target-lexicon", @@ -2465,9 +2438,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb7dec17e17766b46bca4f1a4215a85006b4c2ecde122076c562dd058da6cf1" +checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa" dependencies = [ "libc", "pyo3-build-config", @@ -2486,26 +2459,27 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f738b4e40d50b5711957f142878cfa0f28e054aa0ebdfc3fd137a843f74ed3" +checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] name = "pyo3-macros-backend" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc910d4851847827daf9d6cdd4a823fbdaab5b8818325c5e97a86da79e8881f" +checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", + "pyo3-build-config", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -2564,9 +2538,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -2616,9 +2590,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -2633,20 +2607,20 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "58b48d98d932f4ee75e541614d32a7f44c889b72bd9c2e04d95edd135989df88" dependencies = [ "base64", "bytes", - "encoding_rs", "futures-core", "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", + "http", + "http-body", + "http-body-util", + "hyper", "hyper-rustls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2656,12 +2630,12 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", - "rustls-pemfile", + "rustls-pemfile 1.0.4", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-rustls", "tower-service", @@ -2725,11 +2699,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -2738,24 +2712,27 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 2.1.1", + "rustls-pki-types", "schannel", "security-framework", ] @@ -2769,11 +2746,27 @@ dependencies = [ "base64", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" + [[package]] name = "rustls-platform-verifier" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c57b5de012da34087f2fe711fa29770f9a7abdde660b01bac3c9dbdee91b84" +checksum = "2c35b9a497e588f1fb2e1d18a0d46a6d057710f34c3da7084b27353b319453cc" dependencies = [ "core-foundation", "core-foundation-sys", @@ -2798,11 +2791,12 @@ checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad" [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -2898,17 +2892,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", -] - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", + "syn 2.0.53", ] [[package]] @@ -2959,22 +2943,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -2990,9 +2974,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -3018,7 +3002,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -3044,11 +3028,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.5", "itoa", "ryu", "serde", @@ -3143,9 +3127,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smawk" @@ -3155,12 +3139,12 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3221,11 +3205,11 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -3256,9 +3240,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -3291,38 +3275,17 @@ dependencies = [ "walkdir", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -3343,29 +3306,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3442,16 +3405,17 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ "rustls", + "rustls-pki-types", "tokio", ] @@ -3480,9 +3444,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -3501,17 +3465,39 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.5", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3524,6 +3510,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-core", ] @@ -3600,9 +3587,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -3655,7 +3642,7 @@ dependencies = [ "fs-err", "glob", "goblin", - "heck", + "heck 0.4.1", "once_cell", "paste", "serde", @@ -3684,7 +3671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72775b3afa6adb30e0c92b3107858d2fcb0ff1a417ac242db1f648b0e2dd0ef2" dependencies = [ "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] @@ -3717,7 +3704,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.49", + "syn 2.0.53", "toml 0.5.11", "uniffi_build", "uniffi_meta", @@ -3769,9 +3756,9 @@ checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -3798,9 +3785,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "serde", @@ -3820,9 +3807,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -3845,9 +3832,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "serde", @@ -3857,24 +3844,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -3884,9 +3871,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3894,28 +3881,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-bindgen-test" -version = "0.3.41" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143ddeb4f833e2ed0d252e618986e18bfc7b0e52f2d28d77d05b2f045dd8eb61" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" dependencies = [ "console_error_panic_hook", "js-sys", @@ -3927,20 +3914,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.41" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5211b7550606857312bba1d978a8ec75692eae187becc5e680444fffc5e6f89" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -3948,9 +3935,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "weedle2" @@ -3998,7 +3988,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -4016,7 +4006,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -4036,17 +4026,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -4057,9 +4047,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -4069,9 +4059,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -4081,9 +4071,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -4093,9 +4083,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -4105,9 +4095,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -4117,9 +4107,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -4129,15 +4119,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -4163,9 +4153,9 @@ dependencies = [ "base64", "deadpool", "futures", - "http 1.0.0", + "http", "http-body-util", - "hyper 1.1.0", + "hyper", "hyper-util", "log", "once_cell", @@ -4193,7 +4183,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.53", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ca8d49461..f137d7898 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ bitwarden-crypto = { path = "crates/bitwarden-crypto", version = "=0.4.0" } bitwarden-exporters = { path = "crates/bitwarden-exporters", version = "=0.4.0" } bitwarden-generators = { path = "crates/bitwarden-generators", version = "=0.4.0" } +[workspace.lints.clippy] +unwrap_used = "deny" + # Compile all dependencies with some optimizations when building this crate on debug # This slows down clean builds by about 50%, but the resulting binaries can be orders of magnitude faster # As clean builds won't occur very often, this won't slow down the development process diff --git a/README.md b/README.md index cdc52a7cb..a1ef88b7d 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,9 @@ This project uses customized templates which lives in the `support/openapi-templ These templates resolves some outstanding issues we've experienced with the rust generator. But we strive towards modifying the templates as little as possible to ease future upgrades. +Note: If you don't have the nightly toolchain installed, the `build-api.sh` script will install it +for you. + ## Tests Many of the SDK tests are based on encrypted data provided by the other Bitwarden clients. In order diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..a29e019ac --- /dev/null +++ b/clippy.toml @@ -0,0 +1,2 @@ +allow-unwrap-in-tests=true +allow-expect-in-tests=true diff --git a/crates/bitwarden-api-api/Cargo.toml b/crates/bitwarden-api-api/Cargo.toml index c0eb0c3c5..a06a0f0e5 100644 --- a/crates/bitwarden-api-api/Cargo.toml +++ b/crates/bitwarden-api-api/Cargo.toml @@ -22,7 +22,7 @@ serde_repr = ">=0.1.12, <0.2" url = ">=2.3.1, <3" uuid = { version = ">=1.3.3, <2", features = ["serde"] } [dependencies.reqwest] -version = ">=0.11.18, <0.12" +version = ">=0.12, <0.13" features = ["json", "multipart"] default-features = false diff --git a/crates/bitwarden-api-identity/Cargo.toml b/crates/bitwarden-api-identity/Cargo.toml index c3b345300..e5a49efac 100644 --- a/crates/bitwarden-api-identity/Cargo.toml +++ b/crates/bitwarden-api-identity/Cargo.toml @@ -22,7 +22,7 @@ serde_repr = ">=0.1.12, <0.2" url = ">=2.3.1, <3" uuid = { version = ">=1.3.3, <2", features = ["serde"] } [dependencies.reqwest] -version = ">=0.11.18, <0.12" +version = ">=0.12, <0.13" features = ["json", "multipart"] default-features = false diff --git a/crates/bitwarden-c/Cargo.toml b/crates/bitwarden-c/Cargo.toml index e3f966ee1..47d4ff5cd 100644 --- a/crates/bitwarden-c/Cargo.toml +++ b/crates/bitwarden-c/Cargo.toml @@ -21,3 +21,6 @@ bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } [dependencies] env_logger = ">=0.10.0, <0.12" + +[lints] +workspace = true diff --git a/crates/bitwarden-c/src/c.rs b/crates/bitwarden-c/src/c.rs index 934b20359..32abe3dc0 100644 --- a/crates/bitwarden-c/src/c.rs +++ b/crates/bitwarden-c/src/c.rs @@ -11,7 +11,8 @@ pub async extern "C" fn run_command( client_ptr: *const Client, ) -> *mut c_char { let client = unsafe { ffi_ref!(client_ptr) }; - let input_str = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr).to_bytes() }).unwrap(); + let input_str = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr) }.to_bytes()) + .expect("Input should be a valid string"); let result = client.run_command(input_str).await; match std::ffi::CString::new(result) { @@ -28,8 +29,8 @@ pub extern "C" fn init(c_str_ptr: *const c_char) -> *mut Client { if c_str_ptr.is_null() { box_ptr!(Client::new(None)) } else { - let input_string = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr).to_bytes() }) - .unwrap() + let input_string = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr) }.to_bytes()) + .expect("Input should be a valid string") .to_owned(); box_ptr!(Client::new(Some(input_string))) } diff --git a/crates/bitwarden-cli/Cargo.toml b/crates/bitwarden-cli/Cargo.toml index 2a2cb3a98..69a21704c 100644 --- a/crates/bitwarden-cli/Cargo.toml +++ b/crates/bitwarden-cli/Cargo.toml @@ -15,3 +15,6 @@ clap = { version = "4.5.1", features = ["derive"] } color-eyre = "0.6" inquire = "0.6.2" supports-color = "3.0.0" + +[lints] +workspace = true diff --git a/crates/bitwarden-crypto/Cargo.toml b/crates/bitwarden-crypto/Cargo.toml index 124725340..e42ca686f 100644 --- a/crates/bitwarden-crypto/Cargo.toml +++ b/crates/bitwarden-crypto/Cargo.toml @@ -21,7 +21,7 @@ mobile = ["dep:uniffi"] # Mobile-specific features [dependencies] aes = { version = ">=0.8.2, <0.9", features = ["zeroize"] } argon2 = { version = ">=0.5.0, <0.6", features = [ - "alloc", + "std", "zeroize", ], default-features = false } base64 = ">=0.21.2, <0.22" @@ -48,3 +48,6 @@ zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } [dev-dependencies] rand_chacha = "0.3.1" serde_json = ">=1.0.96, <2.0" + +[lints] +workspace = true diff --git a/crates/bitwarden-crypto/src/aes.rs b/crates/bitwarden-crypto/src/aes.rs index ee76f9936..dde588563 100644 --- a/crates/bitwarden-crypto/src/aes.rs +++ b/crates/bitwarden-crypto/src/aes.rs @@ -149,7 +149,8 @@ pub fn decrypt_aes128_hmac( /// Generate a MAC using HMAC-SHA256. fn generate_mac(mac_key: &[u8], iv: &[u8], data: &[u8]) -> Result<[u8; 32]> { - let mut hmac = PbkdfSha256Hmac::new_from_slice(mac_key).expect("HMAC can take key of any size"); + let mut hmac = + PbkdfSha256Hmac::new_from_slice(mac_key).expect("hmac new_from_slice should not fail"); hmac.update(iv); hmac.update(data); let mac: [u8; PBKDF_SHA256_HMAC_OUT_SIZE] = (*hmac.finalize().into_bytes()) diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index d5489fc96..d312882d7 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -119,15 +119,15 @@ impl EncString { match enc_type { 0 => { check_length(buf, 18)?; - let iv = buf[1..17].try_into().unwrap(); + let iv = buf[1..17].try_into().expect("Valid length"); let data = buf[17..].to_vec(); Ok(EncString::AesCbc256_B64 { iv, data }) } 1 | 2 => { check_length(buf, 50)?; - let iv = buf[1..17].try_into().unwrap(); - let mac = buf[17..49].try_into().unwrap(); + let iv = buf[1..17].try_into().expect("Valid length"); + let mac = buf[17..49].try_into().expect("Valid length"); let data = buf[49..].to_vec(); if enc_type == 1 { @@ -290,14 +290,16 @@ mod tests { use schemars::schema_for; use super::EncString; - use crate::{derive_symmetric_key, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey}; + use crate::{ + derive_symmetric_key, KeyDecryptable, KeyEncryptable, SensitiveString, SymmetricCryptoKey, + }; #[test] fn test_enc_string_roundtrip() { let key = derive_symmetric_key("test"); - let test_string = "encrypted_test_string".to_string(); - let cipher = test_string.clone().encrypt_with_key(&key).unwrap(); + let test_string = "encrypted_test_string"; + let cipher = test_string.to_owned().encrypt_with_key(&key).unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); @@ -390,8 +392,8 @@ mod tests { #[test] fn test_decrypt_cbc256() { - let key = "hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe08="; - let key: SymmetricCryptoKey = key.parse().unwrap(); + let key = SensitiveString::test("hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe08="); + let key = SymmetricCryptoKey::try_from(key).unwrap(); let enc_str = "0.NQfjHLr6za7VQVAbrpL81w==|wfrjmyJ0bfwkQlySrhw8dA=="; let enc_string: EncString = enc_str.parse().unwrap(); @@ -403,8 +405,8 @@ mod tests { #[test] fn test_decrypt_cbc128_hmac() { - let key = "Gt1aZ8kTTgkF80bLtb7LiMZBcxEA2FA5mbvV4x7K208="; - let key: SymmetricCryptoKey = key.parse().unwrap(); + let key = SensitiveString::test("Gt1aZ8kTTgkF80bLtb7LiMZBcxEA2FA5mbvV4x7K208="); + let key = SymmetricCryptoKey::try_from(key).unwrap(); let enc_str = "1.CU/oG4VZuxbHoZSDZjCLQw==|kb1HGwAk+fQ275ORfLf5Ew==|8UaEYHyqRZcG37JWhYBOBdEatEXd1u1/wN7OuImolcM="; let enc_string: EncString = enc_str.parse().unwrap(); diff --git a/crates/bitwarden-crypto/src/error.rs b/crates/bitwarden-crypto/src/error.rs index cf9a9b048..7cfb354d7 100644 --- a/crates/bitwarden-crypto/src/error.rs +++ b/crates/bitwarden-crypto/src/error.rs @@ -28,6 +28,9 @@ pub enum CryptoError { #[error("Fingerprint error, {0}")] FingerprintError(#[from] FingerprintError), + #[error("Argon2 error, {0}")] + ArgonError(#[from] argon2::Error), + #[error("Number is zero")] ZeroNumber, } diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index d3e69d577..b88435dbc 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -51,7 +51,10 @@ fn hash_word(hash: [u8; 32]) -> Result { let remainder = hash_number.clone() % EFF_LONG_WORD_LIST.len(); hash_number /= EFF_LONG_WORD_LIST.len(); - phrase.push(EFF_LONG_WORD_LIST[remainder.to_usize().unwrap()].to_string()); + let index = remainder + .to_usize() + .expect("Remainder is less than EFF_LONG_WORD_LIST.len()"); + phrase.push(EFF_LONG_WORD_LIST[index].to_string()); } Ok(phrase.join("-")) diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 876ae7b75..d33e0dc32 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,8 +1,6 @@ -use std::str::FromStr; - use crate::{ - error::Result, AsymmetricCryptoKey, AsymmetricEncString, CryptoError, EncString, - KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + error::Result, AsymmetricCryptoKey, AsymmetricEncString, CryptoError, DecryptedVec, EncString, + KeyDecryptable, KeyEncryptable, SensitiveString, SymmetricCryptoKey, }; /// Device Key @@ -16,7 +14,7 @@ pub struct DeviceKey(SymmetricCryptoKey); #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct TrustDeviceResponse { /// Base64 encoded device key - pub device_key: String, + pub device_key: SensitiveString, /// UserKey encrypted with DevicePublicKey pub protected_user_key: AsymmetricEncString, /// DevicePrivateKey encrypted with [DeviceKey] @@ -40,7 +38,7 @@ impl DeviceKey { let data = user_key.to_vec(); let protected_user_key = - AsymmetricEncString::encrypt_rsa2048_oaep_sha1(&data, &device_private_key)?; + AsymmetricEncString::encrypt_rsa2048_oaep_sha1(data.expose(), &device_private_key)?; let protected_device_public_key = device_private_key .to_public_der()? @@ -65,25 +63,25 @@ impl DeviceKey { protected_user_key: AsymmetricEncString, ) -> Result { let device_private_key: Vec = protected_device_private_key.decrypt_with_key(&self.0)?; - let device_private_key = AsymmetricCryptoKey::from_der(device_private_key.as_slice())?; + let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?; - let mut dec: Vec = protected_user_key.decrypt_with_key(&device_private_key)?; - let user_key: SymmetricCryptoKey = dec.as_mut_slice().try_into()?; + let dec: Vec = protected_user_key.decrypt_with_key(&device_private_key)?; + let dec = DecryptedVec::new(Box::new(dec)); + let user_key = SymmetricCryptoKey::try_from(dec)?; Ok(user_key) } - fn to_base64(&self) -> String { + fn to_base64(&self) -> SensitiveString { self.0.to_base64() } } -impl FromStr for DeviceKey { - type Err = CryptoError; +impl TryFrom for DeviceKey { + type Error = CryptoError; - fn from_str(s: &str) -> Result { - let key = s.parse::()?; - Ok(DeviceKey(key)) + fn try_from(value: SensitiveString) -> Result { + SymmetricCryptoKey::try_from(value).map(DeviceKey) } } @@ -98,10 +96,8 @@ mod tests { let result = DeviceKey::trust_device(&key).unwrap(); - let decrypted = result - .device_key - .parse::() - .unwrap() + let device_key = DeviceKey::try_from(result.device_key).unwrap(); + let decrypted = device_key .decrypt_user_key( result.protected_device_private_key, result.protected_user_key, diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index aff403c59..b6921fc6b 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -5,7 +5,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use super::utils::{derive_kdf_key, stretch_kdf_key}; -use crate::{util, EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey}; +use crate::{util, CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey}; #[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -67,8 +67,11 @@ impl MasterKey { let stretched_key = stretch_kdf_key(&self.0)?; EncString::encrypt_aes256_hmac( - user_key.to_vec().as_slice(), - stretched_key.mac_key.as_ref().unwrap(), + user_key.to_vec().expose(), + stretched_key + .mac_key + .as_ref() + .ok_or(CryptoError::InvalidMac)?, &stretched_key.key, ) } diff --git a/crates/bitwarden-crypto/src/keys/mod.rs b/crates/bitwarden-crypto/src/keys/mod.rs index 5bfd32b8b..435f1011b 100644 --- a/crates/bitwarden-crypto/src/keys/mod.rs +++ b/crates/bitwarden-crypto/src/keys/mod.rs @@ -1,5 +1,5 @@ mod key_encryptable; -pub use key_encryptable::{KeyDecryptable, KeyEncryptable}; +pub use key_encryptable::{CryptoKey, KeyDecryptable, KeyEncryptable}; mod master_key; pub use master_key::{HashPurpose, Kdf, MasterKey}; mod shareable_key; diff --git a/crates/bitwarden-crypto/src/keys/shareable_key.rs b/crates/bitwarden-crypto/src/keys/shareable_key.rs index fbb2d2e44..e84e05ca3 100644 --- a/crates/bitwarden-crypto/src/keys/shareable_key.rs +++ b/crates/bitwarden-crypto/src/keys/shareable_key.rs @@ -2,9 +2,12 @@ use std::pin::Pin; use aes::cipher::typenum::U64; use generic_array::GenericArray; -use hmac::{Hmac, Mac}; +use hmac::Mac; -use crate::{keys::SymmetricCryptoKey, util::hkdf_expand}; +use crate::{ + keys::SymmetricCryptoKey, + util::{hkdf_expand, PbkdfSha256Hmac}, +}; /// Derive a shareable key using hkdf from secret and name. /// @@ -19,15 +22,16 @@ pub fn derive_shareable_key( // TODO: Are these the final `key` and `info` parameters or should we change them? I followed // the pattern used for sends - let res = Hmac::::new_from_slice(format!("bitwarden-{}", name).as_bytes()) - .unwrap() + let res = PbkdfSha256Hmac::new_from_slice(format!("bitwarden-{}", name).as_bytes()) + .expect("hmac new_from_slice should not fail") .chain_update(secret) .finalize() .into_bytes(); - let mut key: Pin>> = hkdf_expand(&res, info).unwrap(); + let mut key: Pin>> = + hkdf_expand(&res, info).expect("Input is a valid size"); - SymmetricCryptoKey::try_from(key.as_mut_slice()).unwrap() + SymmetricCryptoKey::try_from(key.as_mut_slice()).expect("Key is a valid size") } #[cfg(test)] @@ -37,9 +41,9 @@ mod tests { #[test] fn test_derive_shareable_key() { let key = derive_shareable_key(*b"&/$%F1a895g67HlX", "test_key", None); - assert_eq!(key.to_base64(), "4PV6+PcmF2w7YHRatvyMcVQtI7zvCyssv/wFWmzjiH6Iv9altjmDkuBD1aagLVaLezbthbSe+ktR+U6qswxNnQ=="); + assert_eq!(key.to_base64().expose(), "4PV6+PcmF2w7YHRatvyMcVQtI7zvCyssv/wFWmzjiH6Iv9altjmDkuBD1aagLVaLezbthbSe+ktR+U6qswxNnQ=="); let key = derive_shareable_key(*b"67t9b5g67$%Dh89n", "test_key", Some("test")); - assert_eq!(key.to_base64(), "F9jVQmrACGx9VUPjuzfMYDjr726JtL300Y3Yg+VYUnVQtQ1s8oImJ5xtp1KALC9h2nav04++1LDW4iFD+infng=="); + assert_eq!(key.to_base64().expose(), "F9jVQmrACGx9VUPjuzfMYDjr726JtL300Y3Yg+VYUnVQtQ1s8oImJ5xtp1KALC9h2nav04++1LDW4iFD+infng=="); } } diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 0c165bb40..23d09cbca 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -1,13 +1,13 @@ -use std::{pin::Pin, str::FromStr}; +use std::pin::Pin; use aes::cipher::typenum::U32; -use base64::{engine::general_purpose::STANDARD, Engine}; +use base64::engine::general_purpose::STANDARD; use generic_array::GenericArray; use rand::Rng; -use zeroize::{Zeroize, Zeroizing}; +use zeroize::Zeroize; use super::key_encryptable::CryptoKey; -use crate::CryptoError; +use crate::{CryptoError, SensitiveString, SensitiveVec}; /// A symmetric encryption key. Used to encrypt and decrypt [`EncString`](crate::EncString) pub struct SymmetricCryptoKey { @@ -59,31 +59,35 @@ impl SymmetricCryptoKey { self.key.len() + self.mac_key.as_ref().map_or(0, |mac| mac.len()) } - pub fn to_base64(&self) -> String { - let mut buf = self.to_vec(); - - let result = STANDARD.encode(&buf); - buf.zeroize(); - result + pub fn to_base64(&self) -> SensitiveString { + self.to_vec().encode_base64(STANDARD) } - pub fn to_vec(&self) -> Zeroizing> { - let mut buf = Vec::with_capacity(self.total_len()); + pub fn to_vec(&self) -> SensitiveVec { + let mut buf = SensitiveVec::new(Box::new(Vec::with_capacity(self.total_len()))); - buf.extend_from_slice(&self.key); + buf.expose_mut().extend_from_slice(&self.key); if let Some(mac) = &self.mac_key { - buf.extend_from_slice(mac); + buf.expose_mut().extend_from_slice(mac); } - Zeroizing::new(buf) + buf } } -impl FromStr for SymmetricCryptoKey { - type Err = CryptoError; +impl TryFrom for SymmetricCryptoKey { + type Error = CryptoError; - fn from_str(s: &str) -> Result { - let mut bytes = STANDARD.decode(s).map_err(|_| CryptoError::InvalidKey)?; - SymmetricCryptoKey::try_from(bytes.as_mut_slice()) + fn try_from(value: SensitiveString) -> Result { + SymmetricCryptoKey::try_from(value.decode_base64(STANDARD)?) + } +} + +impl TryFrom for SymmetricCryptoKey { + type Error = CryptoError; + + fn try_from(mut value: SensitiveVec) -> Result { + let val = value.expose_mut(); + SymmetricCryptoKey::try_from(val.as_mut_slice()) } } @@ -138,19 +142,18 @@ pub fn derive_symmetric_key(name: &str) -> SymmetricCryptoKey { #[cfg(test)] mod tests { - use std::str::FromStr; - use super::{derive_symmetric_key, SymmetricCryptoKey}; + use crate::SensitiveString; #[test] fn test_symmetric_crypto_key() { let key = derive_symmetric_key("test"); - let key2 = SymmetricCryptoKey::from_str(&key.to_base64()).unwrap(); + let key2 = SymmetricCryptoKey::try_from(key.to_base64()).unwrap(); assert_eq!(key.key, key2.key); assert_eq!(key.mac_key, key2.mac_key); - let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ=="; - let key2 = SymmetricCryptoKey::from_str(key).unwrap(); + let key = SensitiveString::test("UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ=="); + let key2 = SymmetricCryptoKey::try_from(key.clone()).unwrap(); assert_eq!(key, key2.to_base64()); } } diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index d83e212d0..a2df336e1 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -25,16 +25,13 @@ pub(super) fn derive_kdf_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result Result { .to_pkcs8_der() .map_err(|_| RsaError::CreatePrivateKey)?; - let protected = - EncString::encrypt_aes256_hmac(pkcs.as_bytes(), key.mac_key.as_ref().unwrap(), &key.key)?; + let protected = EncString::encrypt_aes256_hmac( + pkcs.as_bytes(), + key.mac_key.as_ref().ok_or(CryptoError::InvalidMac)?, + &key.key, + )?; Ok(RsaKeyPair { public: b64, diff --git a/crates/bitwarden-crypto/src/sensitive/decrypted.rs b/crates/bitwarden-crypto/src/sensitive/decrypted.rs new file mode 100644 index 000000000..02096d081 --- /dev/null +++ b/crates/bitwarden-crypto/src/sensitive/decrypted.rs @@ -0,0 +1,16 @@ +use zeroize::Zeroize; + +use crate::{CryptoError, CryptoKey, KeyEncryptable, Sensitive}; + +/// Type alias for a [`Sensitive`] value to denote decrypted data. +pub type Decrypted = Sensitive; +pub type DecryptedVec = Decrypted>; +pub type DecryptedString = Decrypted; + +impl + Zeroize + Clone, Key: CryptoKey, Output> + KeyEncryptable for Decrypted +{ + fn encrypt_with_key(self, key: &Key) -> Result { + self.value.clone().encrypt_with_key(key) + } +} diff --git a/crates/bitwarden-crypto/src/sensitive/mod.rs b/crates/bitwarden-crypto/src/sensitive/mod.rs new file mode 100644 index 000000000..0da2329b9 --- /dev/null +++ b/crates/bitwarden-crypto/src/sensitive/mod.rs @@ -0,0 +1,5 @@ +#[allow(clippy::module_inception)] +mod sensitive; +pub use sensitive::{Sensitive, SensitiveString, SensitiveVec}; +mod decrypted; +pub use decrypted::{Decrypted, DecryptedString, DecryptedVec}; diff --git a/crates/bitwarden-crypto/src/sensitive/sensitive.rs b/crates/bitwarden-crypto/src/sensitive/sensitive.rs new file mode 100644 index 000000000..996b20330 --- /dev/null +++ b/crates/bitwarden-crypto/src/sensitive/sensitive.rs @@ -0,0 +1,220 @@ +use std::{ + borrow::Cow, + fmt::{self, Formatter}, +}; + +use schemars::JsonSchema; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::CryptoError; + +/// Wrapper for sensitive values which makes a best effort to enforce zeroization of the inner value +/// on drop. The inner value exposes a [`Sensitive::expose`] method which returns a reference to the +/// inner value. Care must be taken to avoid accidentally exposing the inner value through copying +/// or cloning. +/// +/// Internally [`Sensitive`] contains a [`Box`] which ensures the value is placed on the heap. It +/// implements the [`Drop`] trait which calls `zeroize` on the inner value. +#[derive(PartialEq, Clone, Zeroize, ZeroizeOnDrop)] +pub struct Sensitive { + pub(super) value: Box, +} + +/// Important: This type does not protect against reallocations made by the Vec. +/// This means that if you insert any elements past the capacity, the data will be copied to a +/// new allocation and the old allocation will not be zeroized. +/// To avoid this, use Vec::with_capacity to preallocate the capacity you need. +pub type SensitiveVec = Sensitive>; + +/// Important: This type does not protect against reallocations made by the String. +/// This means that if you insert any characters past the capacity, the data will be copied to a +/// new allocation and the old allocation will not be zeroized. +/// To avoid this, use String::with_capacity to preallocate the capacity you need. +pub type SensitiveString = Sensitive; + +impl Sensitive { + /// Create a new [`Sensitive`] value. In an attempt to avoid accidentally placing this on the + /// stack it only accepts a [`Box`] value. The rust compiler should be able to optimize away the + /// initial stack allocation presuming the value is not used before being boxed. + pub fn new(value: Box) -> Self { + Self { value } + } + + /// Expose the inner value. By exposing the inner value, you take responsibility for ensuring + /// that any copy of the value is zeroized. + pub fn expose(&self) -> &V { + &self.value + } + + /// Expose the inner value mutable. By exposing the inner value, you take responsibility for + /// ensuring that any copy of the value is zeroized. + pub fn expose_mut(&mut self) -> &mut V { + &mut self.value + } +} + +/// Helper to convert a `Sensitive>` to a `Sensitive`, care is taken to ensure any +/// intermediate copies are zeroed to avoid leaking sensitive data. +impl TryFrom for SensitiveString { + type Error = CryptoError; + + fn try_from(mut v: SensitiveVec) -> Result { + let value = std::mem::take(&mut v.value); + + let rtn = String::from_utf8(*value).map_err(|_| CryptoError::InvalidUtf8String); + rtn.map(|v| Sensitive::new(Box::new(v))) + } +} + +impl SensitiveString { + pub fn decode_base64(self, engine: T) -> Result { + // Prevent accidental copies by allocating the full size + let len = base64::decoded_len_estimate(self.value.len()); + let mut value = SensitiveVec::new(Box::new(Vec::with_capacity(len))); + + engine + .decode_vec(self.value.as_ref(), &mut value.value) + .map_err(|_| CryptoError::InvalidKey)?; + + Ok(value) + } +} + +impl SensitiveVec { + pub fn encode_base64(self, engine: T) -> SensitiveString { + use base64::engine::Config; + + // Prevent accidental copies by allocating the full size + let padding = engine.config().encode_padding(); + let len = base64::encoded_len(self.value.len(), padding).expect("Valid length"); + let mut value = SensitiveString::new(Box::new(String::with_capacity(len))); + + engine.encode_string(self.value.as_ref(), &mut value.value); + + value + } +} + +impl Default for Sensitive { + fn default() -> Self { + Self::new(Box::default()) + } +} + +impl fmt::Debug for Sensitive { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Sensitive") + .field("type", &std::any::type_name::()) + .field("value", &"********") + .finish() + } +} + +/// Unfortunately once we serialize a `SensitiveString` we can't control the future memory. +impl Serialize for Sensitive { + fn serialize(&self, serializer: S) -> Result { + self.value.serialize(serializer) + } +} + +impl<'de, V: Zeroize + Deserialize<'de>> Deserialize<'de> for Sensitive { + fn deserialize>(deserializer: D) -> Result { + Ok(Self::new(Box::new(V::deserialize(deserializer)?))) + } +} + +/// Transparently expose the inner value for serialization +impl JsonSchema for Sensitive { + fn schema_name() -> String { + V::schema_name() + } + + fn schema_id() -> Cow<'static, str> { + V::schema_id() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + V::json_schema(gen) + } +} + +impl Sensitive { + // We use a lot of `&str` in our tests, so we expose this helper + // to make it easier. + // IMPORTANT: This should not be used outside of test code + // Note that we can't just mark it with #[cfg(test)] because that only applies + // when testing this crate, not when testing other crates that depend on it. + // By at least limiting it to &'static str we should be able to avoid accidental usages + pub fn test(value: &'static str) -> Self { + Self::new(Box::new(value.to_string())) + } +} + +#[cfg(test)] +mod tests { + use schemars::schema_for; + + use super::*; + + #[test] + fn test_debug() { + let string = Sensitive::test("test"); + assert_eq!( + format!("{:?}", string), + "Sensitive { type: \"alloc::string::String\", value: \"********\" }" + ); + + let vector = Sensitive::new(Box::new(vec![1, 2, 3])); + assert_eq!( + format!("{:?}", vector), + "Sensitive { type: \"alloc::vec::Vec\", value: \"********\" }" + ); + } + + #[test] + fn test_schemars() { + #[derive(JsonSchema)] + struct TestStruct { + #[allow(dead_code)] + s: SensitiveString, + #[allow(dead_code)] + v: SensitiveVec, + } + + let schema = schema_for!(TestStruct); + let json = serde_json::to_string_pretty(&schema).unwrap(); + let expected = r##"{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TestStruct", + "type": "object", + "required": ["s", "v"], + "properties": { + "s": { + "$ref": "#/definitions/String" + }, + "v": { + "$ref": "#/definitions/Array_of_uint8" + } + }, + "definitions": { + "Array_of_uint8": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "String": { + "type": "string" + } + } + }"##; + + assert_eq!( + json.parse::().unwrap(), + expected.parse::().unwrap() + ); + } +} diff --git a/crates/bitwarden-crypto/src/uniffi_support.rs b/crates/bitwarden-crypto/src/uniffi_support.rs index 7f1249da0..f99b55da5 100644 --- a/crates/bitwarden-crypto/src/uniffi_support.rs +++ b/crates/bitwarden-crypto/src/uniffi_support.rs @@ -1,6 +1,8 @@ use std::{num::NonZeroU32, str::FromStr}; -use crate::{AsymmetricEncString, CryptoError, EncString, UniffiCustomTypeConverter}; +use crate::{ + AsymmetricEncString, CryptoError, EncString, SensitiveString, UniffiCustomTypeConverter, +}; uniffi::custom_type!(NonZeroU32, u32); @@ -43,3 +45,24 @@ impl UniffiCustomTypeConverter for AsymmetricEncString { obj.to_string() } } + +uniffi::custom_type!(SensitiveString, String); + +impl UniffiCustomTypeConverter for SensitiveString { + type Builtin = String; + + fn into_custom(val: Self::Builtin) -> uniffi::Result { + Ok(SensitiveString::new(Box::new(val))) + } + + fn from_custom(obj: Self) -> Self::Builtin { + obj.expose().to_owned() + } +} + +/// Uniffi doesn't seem to be generating the SensitiveString unless it's being used by +/// a record somewhere. This is a workaround to make sure the type is generated. +#[derive(uniffi::Record)] +struct SupportSensitiveString { + sensitive_string: SensitiveString, +} diff --git a/crates/bitwarden-exporters/Cargo.toml b/crates/bitwarden-exporters/Cargo.toml index 4ef8018b4..fc118b922 100644 --- a/crates/bitwarden-exporters/Cargo.toml +++ b/crates/bitwarden-exporters/Cargo.toml @@ -27,3 +27,6 @@ serde = { version = ">=1.0, <2.0", features = ["derive"] } serde_json = ">=1.0.96, <2.0" thiserror = ">=1.0.40, <2.0" uuid = { version = ">=1.3.3, <2.0", features = ["serde", "v4"] } + +[lints] +workspace = true diff --git a/crates/bitwarden-exporters/src/csv.rs b/crates/bitwarden-exporters/src/csv.rs index 644eeb030..eb60fca21 100644 --- a/crates/bitwarden-exporters/src/csv.rs +++ b/crates/bitwarden-exporters/src/csv.rs @@ -48,7 +48,7 @@ pub(crate) fn export_csv(folders: Vec, ciphers: Vec) -> Result ResponseIntoString for Response { Ok(ser) => ser, Err(e) => { let error = Response::error(format!("Failed to serialize Response: {}", e)); - serde_json::to_string(&error).unwrap() + serde_json::to_string(&error).expect("Serialize should be infallible") } } } diff --git a/crates/bitwarden-napi/Cargo.toml b/crates/bitwarden-napi/Cargo.toml index e9c420e99..16853dbbf 100644 --- a/crates/bitwarden-napi/Cargo.toml +++ b/crates/bitwarden-napi/Cargo.toml @@ -31,3 +31,6 @@ napi-build = "2.1.0" [profile.release] lto = true + +[lints] +workspace = true diff --git a/crates/bitwarden-py/Cargo.toml b/crates/bitwarden-py/Cargo.toml index 3c6ca5f7b..e9073d802 100644 --- a/crates/bitwarden-py/Cargo.toml +++ b/crates/bitwarden-py/Cargo.toml @@ -29,3 +29,6 @@ pyo3-asyncio = { version = "0.20.0", features = [ "attributes", "tokio-runtime", ] } + +[lints] +workspace = true diff --git a/crates/bitwarden-uniffi/Cargo.toml b/crates/bitwarden-uniffi/Cargo.toml index e0e6e7647..8dfddb820 100644 --- a/crates/bitwarden-uniffi/Cargo.toml +++ b/crates/bitwarden-uniffi/Cargo.toml @@ -32,3 +32,6 @@ uniffi = "=0.26.1" [build-dependencies] uniffi = { version = "=0.26.1", features = ["build"] } + +[lints] +workspace = true diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index 2a451ffdf..62b709045 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use bitwarden::auth::{ password::MasterPasswordPolicyOptions, AuthRequestResponse, RegisterKeyResponse, + RegisterTdeKeyResponse, }; use bitwarden_crypto::{AsymmetricEncString, HashPurpose, Kdf, TrustDeviceResponse}; @@ -78,6 +79,21 @@ impl ClientAuth { .make_register_keys(email, password, kdf)?) } + /// Generate keys needed for TDE process + pub async fn make_register_tde_keys( + &self, + org_public_key: String, + remember_device: bool, + ) -> Result { + Ok(self + .0 + .0 + .write() + .await + .auth() + .make_register_tde_keys(org_public_key, remember_device)?) + } + /// Validate the user password /// /// To retrieve the user's password hash, use [`ClientAuth::hash_password`] with diff --git a/crates/bitwarden-uniffi/src/crypto.rs b/crates/bitwarden-uniffi/src/crypto.rs index 0f877d52e..f3abe694a 100644 --- a/crates/bitwarden-uniffi/src/crypto.rs +++ b/crates/bitwarden-uniffi/src/crypto.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use bitwarden::mobile::crypto::{ DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse, }; -use bitwarden_crypto::{AsymmetricEncString, EncString}; +use bitwarden_crypto::{AsymmetricEncString, EncString, SensitiveString}; use crate::{error::Result, Client}; @@ -40,7 +40,7 @@ impl ClientCrypto { /// Get the uses's decrypted encryption key. Note: It's very important /// to keep this key safe, as it can be used to decrypt all of the user's data - pub async fn get_user_encryption_key(&self) -> Result { + pub async fn get_user_encryption_key(&self) -> Result { Ok(self .0 .0 diff --git a/crates/bitwarden-uniffi/src/uniffi_support.rs b/crates/bitwarden-uniffi/src/uniffi_support.rs index 663d5c41e..a49e347b6 100644 --- a/crates/bitwarden-uniffi/src/uniffi_support.rs +++ b/crates/bitwarden-uniffi/src/uniffi_support.rs @@ -1,7 +1,8 @@ -use bitwarden_crypto::{AsymmetricEncString, EncString}; +use bitwarden_crypto::{AsymmetricEncString, EncString, SensitiveString}; // Forward the type definitions to the main bitwarden crate type DateTime = chrono::DateTime; uniffi::ffi_converter_forward!(DateTime, bitwarden::UniFfiTag, crate::UniFfiTag); uniffi::ffi_converter_forward!(EncString, bitwarden::UniFfiTag, crate::UniFfiTag); uniffi::ffi_converter_forward!(AsymmetricEncString, bitwarden::UniFfiTag, crate::UniFfiTag); +uniffi::ffi_converter_forward!(SensitiveString, bitwarden::UniFfiTag, crate::UniFfiTag); diff --git a/crates/bitwarden-wasm/Cargo.toml b/crates/bitwarden-wasm/Cargo.toml index 01bbe4161..ff4cf13c3 100644 --- a/crates/bitwarden-wasm/Cargo.toml +++ b/crates/bitwarden-wasm/Cargo.toml @@ -29,3 +29,6 @@ wasm-bindgen-futures = "0.4.41" [dev-dependencies] wasm-bindgen-test = "0.3.41" + +[lints] +workspace = true diff --git a/crates/bitwarden/Cargo.toml b/crates/bitwarden/Cargo.toml index 5846d28ae..e064360c8 100644 --- a/crates/bitwarden/Cargo.toml +++ b/crates/bitwarden/Cargo.toml @@ -47,7 +47,7 @@ getrandom = { version = ">=0.2.9, <0.3", features = ["js"] } hmac = ">=0.12.1, <0.13" log = ">=0.4.18, <0.5" rand = ">=0.8.5, <0.9" -reqwest = { version = ">=0.11, <0.12", features = [ +reqwest = { version = ">=0.12, <0.13", features = [ "json", ], default-features = false } schemars = { version = ">=0.8.9, <0.9", features = ["uuid1", "chrono"] } @@ -70,7 +70,7 @@ zxcvbn = ">= 2.2.2, <3.0" reqwest = { version = "*", features = [ "rustls-tls-manual-roots", ], default-features = false } -rustls-platform-verifier = "0.1.0" +rustls-platform-verifier = "0.2.0" [target.'cfg(target_os = "android")'.dependencies] # On android, the use of rustls-platform-verifier is more complicated and going through some changes at the moment, so we fall back to using webpki-roots @@ -84,3 +84,6 @@ rand_chacha = "0.3.1" tokio = { version = "1.36.0", features = ["rt", "macros"] } wiremock = "0.6.0" zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } + +[lints] +workspace = true diff --git a/crates/bitwarden/src/admin_console/policy.rs b/crates/bitwarden/src/admin_console/policy.rs index 7e87b8623..d8ed0b761 100644 --- a/crates/bitwarden/src/admin_console/policy.rs +++ b/crates/bitwarden/src/admin_console/policy.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; use uuid::Uuid; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct Policy { @@ -41,11 +41,11 @@ impl TryFrom for Policy { fn try_from(policy: PolicyResponseModel) -> Result { Ok(Self { - id: policy.id.ok_or(Error::MissingFields)?, - organization_id: policy.organization_id.ok_or(Error::MissingFields)?, - r#type: policy.r#type.ok_or(Error::MissingFields)?.into(), + id: require!(policy.id), + organization_id: require!(policy.organization_id), + r#type: require!(policy.r#type).into(), data: policy.data, - enabled: policy.enabled.ok_or(Error::MissingFields)?, + enabled: require!(policy.enabled), }) } } diff --git a/crates/bitwarden/src/auth/access_token.rs b/crates/bitwarden/src/auth/access_token.rs index 667a78529..4b6f050c8 100644 --- a/crates/bitwarden/src/auth/access_token.rs +++ b/crates/bitwarden/src/auth/access_token.rs @@ -79,7 +79,7 @@ mod tests { "ec2c1d46-6a4b-4751-a310-af9601317f2d" ); assert_eq!(token.client_secret, "C2IgxjjLF7qSshsbwe8JGcbM075YXw"); - assert_eq!(token.encryption_key.to_base64(), "H9/oIRLtL9nGCQOVDjSMoEbJsjWXSOCb3qeyDt6ckzS3FhyboEDWyTP/CQfbIszNmAVg2ExFganG1FVFGXO/Jg=="); + assert_eq!(token.encryption_key.to_base64().expose(), "H9/oIRLtL9nGCQOVDjSMoEbJsjWXSOCb3qeyDt6ckzS3FhyboEDWyTP/CQfbIszNmAVg2ExFganG1FVFGXO/Jg=="); } #[test] diff --git a/crates/bitwarden/src/auth/api/request/mod.rs b/crates/bitwarden/src/auth/api/request/mod.rs index 2b5bde225..263d28357 100644 --- a/crates/bitwarden/src/auth/api/request/mod.rs +++ b/crates/bitwarden/src/auth/api/request/mod.rs @@ -54,7 +54,7 @@ async fn send_identity_connect_request( } let response = request - .body(serde_qs::to_string(&body).unwrap()) + .body(serde_qs::to_string(&body).expect("Serialize should be infallible")) .send() .await?; diff --git a/crates/bitwarden/src/auth/auth_request.rs b/crates/bitwarden/src/auth/auth_request.rs index 18c71afba..04dc9fdce 100644 --- a/crates/bitwarden/src/auth/auth_request.rs +++ b/crates/bitwarden/src/auth/auth_request.rs @@ -92,15 +92,13 @@ pub(crate) fn approve_auth_request( let key = enc.get_key(&None).ok_or(Error::VaultLocked)?; Ok(AsymmetricEncString::encrypt_rsa2048_oaep_sha1( - &key.to_vec(), + key.to_vec().expose(), &public_key, )?) } #[test] fn test_auth_request() { - use zeroize::Zeroizing; - let request = new_auth_request("test@bitwarden.com").unwrap(); let secret: &[u8] = &[ @@ -117,7 +115,7 @@ fn test_auth_request() { let decrypted = auth_request_decrypt_user_key(request.private_key, encrypted).unwrap(); - assert_eq!(decrypted.to_vec(), Zeroizing::new(secret.to_owned())); + assert_eq!(decrypted.to_vec().expose(), secret); } #[cfg(test)] @@ -167,8 +165,8 @@ mod tests { let dec = auth_request_decrypt_user_key(private_key.to_owned(), enc_user_key).unwrap(); assert_eq!( - dec.to_vec().as_ref(), - vec![ + dec.to_vec().expose(), + &[ 201, 37, 234, 213, 21, 75, 40, 70, 149, 213, 234, 16, 19, 251, 162, 245, 161, 74, 34, 245, 211, 151, 211, 192, 95, 10, 117, 50, 88, 223, 23, 157 ] @@ -186,8 +184,8 @@ mod tests { .unwrap(); assert_eq!( - dec.to_vec().as_ref(), - vec![ + dec.to_vec().expose(), + &[ 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, 212, 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, 215, 21, 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, diff --git a/crates/bitwarden/src/auth/client_auth.rs b/crates/bitwarden/src/auth/client_auth.rs index 5f2002133..5029d389b 100644 --- a/crates/bitwarden/src/auth/client_auth.rs +++ b/crates/bitwarden/src/auth/client_auth.rs @@ -20,6 +20,7 @@ use crate::{ MasterPasswordPolicyOptions, }, register::{make_register_keys, register}, + tde::{make_register_tde_keys, RegisterTdeKeyResponse}, AuthRequestResponse, RegisterKeyResponse, RegisterRequest, }, client::Kdf, @@ -73,6 +74,14 @@ impl<'a> ClientAuth<'a> { make_register_keys(email, password, kdf) } + pub fn make_register_tde_keys( + &mut self, + org_public_key: String, + remember_device: bool, + ) -> Result { + make_register_tde_keys(self.client, org_public_key, remember_device) + } + pub async fn register(&mut self, input: &RegisterRequest) -> Result<()> { register(self.client, input).await } diff --git a/crates/bitwarden/src/auth/login/access_token.rs b/crates/bitwarden/src/auth/login/access_token.rs index 46c3f2b50..cca52512b 100644 --- a/crates/bitwarden/src/auth/login/access_token.rs +++ b/crates/bitwarden/src/auth/login/access_token.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; -use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey}; +use base64::engine::general_purpose::STANDARD; +use bitwarden_crypto::{EncString, KeyDecryptable, SensitiveString, SymmetricCryptoKey}; use chrono::Utc; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -14,7 +14,7 @@ use crate::{ AccessToken, JWTToken, }, client::{LoginMethod, ServiceAccountLoginMethod}, - error::{Error, Result}, + error::{require, Error, Result}, secrets_manager::state::{self, ClientState}, Client, }; @@ -59,19 +59,17 @@ pub(crate) async fn login_access_token( #[derive(serde::Deserialize)] struct Payload { #[serde(rename = "encryptionKey")] - encryption_key: String, + encryption_key: SensitiveString, } let payload: Payload = serde_json::from_slice(&decrypted_payload)?; - let mut encryption_key = STANDARD.decode(payload.encryption_key.clone())?; - let encryption_key = SymmetricCryptoKey::try_from(encryption_key.as_mut_slice())?; + let encryption_key = payload.encryption_key.clone().decode_base64(STANDARD)?; + let encryption_key = SymmetricCryptoKey::try_from(encryption_key)?; let access_token_obj: JWTToken = r.access_token.parse()?; // This should always be Some() when logging in with an access token - let organization_id = access_token_obj - .organization - .ok_or(Error::MissingFields)? + let organization_id = require!(access_token_obj.organization) .parse() .map_err(|_| Error::InvalidResponse)?; @@ -125,7 +123,7 @@ fn load_tokens_from_state( let organization_id: Uuid = organization_id .parse() .map_err(|_| "Bad organization id.")?; - let encryption_key: SymmetricCryptoKey = client_state.encryption_key.parse()?; + let encryption_key = SymmetricCryptoKey::try_from(client_state.encryption_key)?; client.set_tokens(client_state.token, None, time_till_expiration as u64); client.initialize_crypto_single_key(encryption_key); diff --git a/crates/bitwarden/src/auth/login/api_key.rs b/crates/bitwarden/src/auth/login/api_key.rs index e161ececd..5d7fdcd96 100644 --- a/crates/bitwarden/src/auth/login/api_key.rs +++ b/crates/bitwarden/src/auth/login/api_key.rs @@ -9,7 +9,7 @@ use crate::{ JWTToken, }, client::{LoginMethod, UserLoginMethod}, - error::Result, + error::{require, Result}, Client, }; @@ -44,8 +44,8 @@ pub(crate) async fn login_api_key( kdf, })); - let user_key: EncString = r.key.as_deref().unwrap().parse().unwrap(); - let private_key: EncString = r.private_key.as_deref().unwrap().parse().unwrap(); + let user_key: EncString = require!(r.key.as_deref()).parse()?; + let private_key: EncString = require!(r.private_key.as_deref()).parse()?; client.initialize_user_crypto(&input.password, user_key, private_key)?; } diff --git a/crates/bitwarden/src/auth/login/auth_request.rs b/crates/bitwarden/src/auth/login/auth_request.rs index 1d85c75d6..0e9cb6795 100644 --- a/crates/bitwarden/src/auth/login/auth_request.rs +++ b/crates/bitwarden/src/auth/login/auth_request.rs @@ -13,7 +13,7 @@ use crate::{ auth_request::new_auth_request, }, client::{LoginMethod, UserLoginMethod}, - error::Result, + error::{require, Result}, mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest}, Client, }; @@ -50,7 +50,7 @@ pub(crate) async fn send_new_auth_request( fingerprint: auth.fingerprint, email, device_identifier, - auth_request_id: res.id.unwrap(), + auth_request_id: require!(res.id), access_code: auth.access_code, private_key: auth.private_key, }) @@ -87,7 +87,7 @@ pub(crate) async fn complete_auth_request( if let IdentityTokenResponse::Authenticated(r) = response { let kdf = Kdf::PBKDF2 { - iterations: NonZeroU32::new(600_000).unwrap(), + iterations: NonZeroU32::new(600_000).expect("Non-zero number"), }; client.set_tokens( @@ -103,11 +103,11 @@ pub(crate) async fn complete_auth_request( let method = match res.master_password_hash { Some(_) => AuthRequestMethod::MasterKey { - protected_master_key: res.key.unwrap().parse().unwrap(), - auth_request_key: r.key.unwrap().parse().unwrap(), + protected_master_key: require!(res.key).parse()?, + auth_request_key: require!(r.key).parse()?, }, None => AuthRequestMethod::UserKey { - protected_user_key: res.key.unwrap().parse().unwrap(), + protected_user_key: require!(res.key).parse()?, }, }; @@ -116,7 +116,7 @@ pub(crate) async fn complete_auth_request( .initialize_user_crypto(InitUserCryptoRequest { kdf_params: kdf, email: auth_req.email, - private_key: r.private_key.unwrap(), + private_key: require!(r.private_key), method: InitUserCryptoMethod::AuthRequest { request_private_key: auth_req.private_key, method, diff --git a/crates/bitwarden/src/auth/login/password.rs b/crates/bitwarden/src/auth/login/password.rs index 02552b70e..8ae9daebc 100644 --- a/crates/bitwarden/src/auth/login/password.rs +++ b/crates/bitwarden/src/auth/login/password.rs @@ -24,7 +24,7 @@ pub(crate) async fn login_password( ) -> Result { use bitwarden_crypto::{EncString, HashPurpose}; - use crate::{auth::determine_password_hash, client::UserLoginMethod}; + use crate::{auth::determine_password_hash, client::UserLoginMethod, error::require}; info!("password logging in"); debug!("{:#?}, {:#?}", client, input); @@ -49,8 +49,8 @@ pub(crate) async fn login_password( kdf: input.kdf.to_owned(), })); - let user_key: EncString = r.key.as_deref().unwrap().parse().unwrap(); - let private_key: EncString = r.private_key.as_deref().unwrap().parse().unwrap(); + let user_key: EncString = require!(r.key.as_deref()).parse()?; + let private_key: EncString = require!(r.private_key.as_deref()).parse()?; client.initialize_user_crypto(&input.password, user_key, private_key)?; } diff --git a/crates/bitwarden/src/auth/mod.rs b/crates/bitwarden/src/auth/mod.rs index 7918694e2..b1fe2dbda 100644 --- a/crates/bitwarden/src/auth/mod.rs +++ b/crates/bitwarden/src/auth/mod.rs @@ -20,6 +20,10 @@ mod auth_request; pub use auth_request::AuthRequestResponse; #[cfg(feature = "mobile")] pub(crate) use auth_request::{auth_request_decrypt_master_key, auth_request_decrypt_user_key}; +#[cfg(feature = "internal")] +mod tde; +#[cfg(feature = "internal")] +pub use tde::RegisterTdeKeyResponse; #[cfg(feature = "internal")] use crate::{client::Kdf, error::Result}; diff --git a/crates/bitwarden/src/auth/tde.rs b/crates/bitwarden/src/auth/tde.rs new file mode 100644 index 000000000..0bbbb904a --- /dev/null +++ b/crates/bitwarden/src/auth/tde.rs @@ -0,0 +1,51 @@ +use base64::{engine::general_purpose::STANDARD, Engine}; +use bitwarden_crypto::{ + AsymmetricEncString, AsymmetricPublicCryptoKey, DeviceKey, EncString, SymmetricCryptoKey, + TrustDeviceResponse, UserKey, +}; + +use crate::{error::Result, Client}; + +/// This function generates a new user key and key pair, initializes the client's crypto with the +/// generated user key, and encrypts the user key with the organization public key for admin +/// password reset. If remember_device is true, it also generates a device key. +pub(super) fn make_register_tde_keys( + client: &mut Client, + org_public_key: String, + remember_device: bool, +) -> Result { + let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(org_public_key)?)?; + + let mut rng = rand::thread_rng(); + + let user_key = UserKey::new(SymmetricCryptoKey::generate(&mut rng)); + let key_pair = user_key.make_key_pair()?; + + let admin_reset = + AsymmetricEncString::encrypt_rsa2048_oaep_sha1(user_key.0.to_vec().expose(), &public_key)?; + + let device_key = if remember_device { + Some(DeviceKey::trust_device(&user_key.0)?) + } else { + None + }; + + client.initialize_user_crypto_decrypted_key(user_key.0, key_pair.private.clone())?; + + Ok(RegisterTdeKeyResponse { + private_key: key_pair.private, + public_key: key_pair.public, + + admin_reset, + device_key, + }) +} + +#[cfg_attr(feature = "mobile", derive(uniffi::Record))] +pub struct RegisterTdeKeyResponse { + pub private_key: EncString, + pub public_key: String, + + pub admin_reset: AsymmetricEncString, + pub device_key: Option, +} diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 883720529..2374d6263 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -108,16 +108,17 @@ impl Client { client_builder } - let external_client = new_client_builder().build().unwrap(); + let external_client = new_client_builder().build().expect("Build should not fail"); let mut headers = header::HeaderMap::new(); headers.append( "Device-Type", - HeaderValue::from_str(&(settings.device_type as u8).to_string()).unwrap(), + HeaderValue::from_str(&(settings.device_type as u8).to_string()) + .expect("All numbers are valid ASCII"), ); let client_builder = new_client_builder().default_headers(headers); - let client = client_builder.build().unwrap(); + let client = client_builder.build().expect("Build should not fail"); let identity = bitwarden_api_identity::apis::configuration::Configuration { base_path: settings.identity_url, @@ -262,10 +263,13 @@ impl Client { user_key, private_key, )?); - Ok(self.encryption_settings.as_ref().unwrap()) + Ok(self + .encryption_settings + .as_ref() + .expect("Value is initialized previously")) } - #[cfg(feature = "mobile")] + #[cfg(feature = "internal")] pub(crate) fn initialize_user_crypto_decrypted_key( &mut self, user_key: SymmetricCryptoKey, @@ -278,7 +282,7 @@ impl Client { Ok(self .encryption_settings .as_ref() - .expect("It was initialized on the previous line")) + .expect("Value is initialized previously")) } #[cfg(feature = "mobile")] @@ -307,7 +311,9 @@ impl Client { key: SymmetricCryptoKey, ) -> &EncryptionSettings { self.encryption_settings = Some(EncryptionSettings::new_single_key(key)); - self.encryption_settings.as_ref().unwrap() + self.encryption_settings + .as_ref() + .expect("Value is initialized previously") } #[cfg(feature = "internal")] @@ -321,7 +327,7 @@ impl Client { .ok_or(Error::VaultLocked)?; enc.set_org_keys(org_keys)?; - Ok(self.encryption_settings.as_ref().unwrap()) + Ok(&*enc) } } diff --git a/crates/bitwarden/src/error.rs b/crates/bitwarden/src/error.rs index bd040e51b..59fae58d2 100644 --- a/crates/bitwarden/src/error.rs +++ b/crates/bitwarden/src/error.rs @@ -24,8 +24,8 @@ pub enum Error { #[error("The response received was invalid and could not be processed")] InvalidResponse, - #[error("The response received was missing some of the required fields")] - MissingFields, + #[error("The response received was missing some of the required fields: {0}")] + MissingFields(&'static str), #[error("Cryptography error, {0}")] Crypto(#[from] bitwarden_crypto::CryptoError), @@ -133,4 +133,18 @@ macro_rules! impl_bitwarden_error { impl_bitwarden_error!(ApiError); impl_bitwarden_error!(IdentityError); +/// This macro is used to require that a value is present or return an error otherwise. +/// It is equivalent to using `val.ok_or(Error::MissingFields)?`, but easier to use and +/// with a more descriptive error message. +/// Note that this macro will return early from the function if the value is not present. +macro_rules! require { + ($val:expr) => { + match $val { + Some(val) => val, + None => return Err($crate::error::Error::MissingFields(stringify!($val))), + } + }; +} +pub(crate) use require; + pub type Result = std::result::Result; diff --git a/crates/bitwarden/src/mobile/client_crypto.rs b/crates/bitwarden/src/mobile/client_crypto.rs index 6ef65975d..26451ffaa 100644 --- a/crates/bitwarden/src/mobile/client_crypto.rs +++ b/crates/bitwarden/src/mobile/client_crypto.rs @@ -1,5 +1,5 @@ #[cfg(feature = "internal")] -use bitwarden_crypto::{AsymmetricEncString, EncString}; +use bitwarden_crypto::{AsymmetricEncString, EncString, SensitiveString}; use crate::Client; #[cfg(feature = "internal")] @@ -28,7 +28,7 @@ impl<'a> ClientCrypto<'a> { } #[cfg(feature = "internal")] - pub async fn get_user_encryption_key(&mut self) -> Result { + pub async fn get_user_encryption_key(&mut self) -> Result { get_user_encryption_key(self.client).await } diff --git a/crates/bitwarden/src/mobile/crypto.rs b/crates/bitwarden/src/mobile/crypto.rs index 214643023..b8891aa09 100644 --- a/crates/bitwarden/src/mobile/crypto.rs +++ b/crates/bitwarden/src/mobile/crypto.rs @@ -2,7 +2,9 @@ use std::collections::HashMap; use bitwarden_crypto::{AsymmetricEncString, EncString}; #[cfg(feature = "internal")] -use bitwarden_crypto::{KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey}; +use bitwarden_crypto::{ + KeyDecryptable, KeyEncryptable, MasterKey, SensitiveString, SymmetricCryptoKey, +}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -86,7 +88,7 @@ pub enum AuthRequestMethod { #[cfg(feature = "internal")] pub async fn initialize_user_crypto(client: &mut Client, req: InitUserCryptoRequest) -> Result<()> { - use bitwarden_crypto::DeviceKey; + use bitwarden_crypto::{DecryptedString, DeviceKey}; use crate::auth::{auth_request_decrypt_master_key, auth_request_decrypt_user_key}; @@ -105,7 +107,8 @@ pub async fn initialize_user_crypto(client: &mut Client, req: InitUserCryptoRequ client.initialize_user_crypto(&password, user_key, private_key)?; } InitUserCryptoMethod::DecryptedKey { decrypted_user_key } => { - let user_key = decrypted_user_key.parse::()?; + let decrypted_user_key = DecryptedString::new(Box::new(decrypted_user_key)); + let user_key = SymmetricCryptoKey::try_from(decrypted_user_key)?; client.initialize_user_crypto_decrypted_key(user_key, private_key)?; } InitUserCryptoMethod::Pin { @@ -138,7 +141,8 @@ pub async fn initialize_user_crypto(client: &mut Client, req: InitUserCryptoRequ protected_device_private_key, device_protected_user_key, } => { - let device_key = device_key.parse::()?; + let device_key = DecryptedString::new(Box::new(device_key)); + let device_key = DeviceKey::try_from(device_key)?; let user_key = device_key .decrypt_user_key(protected_device_private_key, device_protected_user_key)?; @@ -166,7 +170,7 @@ pub async fn initialize_org_crypto(client: &mut Client, req: InitOrgCryptoReques } #[cfg(feature = "internal")] -pub async fn get_user_encryption_key(client: &mut Client) -> Result { +pub async fn get_user_encryption_key(client: &mut Client) -> Result { let user_key = client .get_encryption_settings()? .get_key(&None) @@ -299,7 +303,7 @@ pub(super) fn enroll_admin_password_reset( let key = enc.get_key(&None).ok_or(Error::VaultLocked)?; Ok(AsymmetricEncString::encrypt_rsa2048_oaep_sha1( - &key.to_vec(), + key.to_vec().expose(), &public_key, )?) } @@ -483,7 +487,7 @@ mod tests { #[cfg(feature = "internal")] #[test] fn test_enroll_admin_password_reset() { - use std::{num::NonZeroU32, ops::Deref}; + use std::num::NonZeroU32; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::AsymmetricCryptoKey; @@ -516,10 +520,7 @@ mod tests { .get_encryption_settings() .unwrap() .get_key(&None) - .unwrap() - .to_vec() - .deref() - .clone(); - assert_eq!(decrypted, expected); + .unwrap(); + assert_eq!(&decrypted, expected.to_vec().expose()); } } diff --git a/crates/bitwarden/src/mobile/vault/client_attachments.rs b/crates/bitwarden/src/mobile/vault/client_attachments.rs index c436f10fd..e40721b04 100644 --- a/crates/bitwarden/src/mobile/vault/client_attachments.rs +++ b/crates/bitwarden/src/mobile/vault/client_attachments.rs @@ -40,7 +40,7 @@ impl<'a> ClientAttachments<'a> { decrypted_file_path: &Path, encrypted_file_path: &Path, ) -> Result { - let data = std::fs::read(decrypted_file_path).unwrap(); + let data = std::fs::read(decrypted_file_path)?; let AttachmentEncryptResult { attachment, contents, @@ -73,7 +73,7 @@ impl<'a> ClientAttachments<'a> { encrypted_file_path: &Path, decrypted_file_path: &Path, ) -> Result<()> { - let data = std::fs::read(encrypted_file_path).unwrap(); + let data = std::fs::read(encrypted_file_path)?; let decrypted = self.decrypt_buffer(cipher, attachment, &data).await?; std::fs::write(decrypted_file_path, decrypted)?; Ok(()) diff --git a/crates/bitwarden/src/mobile/vault/client_sends.rs b/crates/bitwarden/src/mobile/vault/client_sends.rs index 45d9a7825..e03432313 100644 --- a/crates/bitwarden/src/mobile/vault/client_sends.rs +++ b/crates/bitwarden/src/mobile/vault/client_sends.rs @@ -36,7 +36,7 @@ impl<'a> ClientSends<'a> { encrypted_file_path: &Path, decrypted_file_path: &Path, ) -> Result<()> { - let data = std::fs::read(encrypted_file_path).unwrap(); + let data = std::fs::read(encrypted_file_path)?; let decrypted = self.decrypt_buffer(send, &data).await?; std::fs::write(decrypted_file_path, decrypted)?; Ok(()) @@ -65,7 +65,7 @@ impl<'a> ClientSends<'a> { decrypted_file_path: &Path, encrypted_file_path: &Path, ) -> Result<()> { - let data = std::fs::read(decrypted_file_path).unwrap(); + let data = std::fs::read(decrypted_file_path)?; let encrypted = self.encrypt_buffer(send, &data).await?; std::fs::write(encrypted_file_path, encrypted)?; Ok(()) diff --git a/crates/bitwarden/src/platform/domain.rs b/crates/bitwarden/src/platform/domain.rs index c903dd5a5..482cb1f59 100644 --- a/crates/bitwarden/src/platform/domain.rs +++ b/crates/bitwarden/src/platform/domain.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct GlobalDomains { @@ -15,9 +15,9 @@ impl TryFrom for GlobalDomains { fn try_from(global_domains: bitwarden_api_api::models::GlobalDomains) -> Result { Ok(Self { - r#type: global_domains.r#type.ok_or(Error::MissingFields)?, - domains: global_domains.domains.ok_or(Error::MissingFields)?, - excluded: global_domains.excluded.ok_or(Error::MissingFields)?, + r#type: require!(global_domains.r#type), + domains: require!(global_domains.domains), + excluded: require!(global_domains.excluded), }) } } diff --git a/crates/bitwarden/src/platform/get_user_api_key.rs b/crates/bitwarden/src/platform/get_user_api_key.rs index 2eaa21894..3e408d926 100644 --- a/crates/bitwarden/src/platform/get_user_api_key.rs +++ b/crates/bitwarden/src/platform/get_user_api_key.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use super::SecretVerificationRequest; use crate::{ client::{LoginMethod, UserLoginMethod}, - error::{Error, Result}, + error::{require, Error, Result}, Client, }; @@ -75,9 +75,7 @@ pub struct UserApiKeyResponse { impl UserApiKeyResponse { pub(crate) fn process_response(response: ApiKeyResponseModel) -> Result { - match response.api_key { - Some(api_key) => Ok(UserApiKeyResponse { api_key }), - None => Err(Error::MissingFields), - } + let api_key = require!(response.api_key); + Ok(UserApiKeyResponse { api_key }) } } diff --git a/crates/bitwarden/src/platform/sync.rs b/crates/bitwarden/src/platform/sync.rs index c1a039137..5a5c6ee82 100644 --- a/crates/bitwarden/src/platform/sync.rs +++ b/crates/bitwarden/src/platform/sync.rs @@ -9,7 +9,7 @@ use super::domain::GlobalDomains; use crate::{ admin_console::Policy, client::{encryption_settings::EncryptionSettings, Client}, - error::{Error, Result}, + error::{require, Error, Result}, vault::{Cipher, Collection, Folder}, }; @@ -25,10 +25,7 @@ pub(crate) async fn sync(client: &mut Client, input: &SyncRequest) -> Result = sync - .profile - .as_ref() - .ok_or(Error::MissingFields)? + let org_keys: Vec<_> = require!(sync.profile.as_ref()) .organizations .as_deref() .unwrap_or_default() @@ -86,8 +83,8 @@ impl SyncResponse { response: SyncResponseModel, enc: &EncryptionSettings, ) -> Result { - let profile = *response.profile.ok_or(Error::MissingFields)?; - let ciphers = response.ciphers.ok_or(Error::MissingFields)?; + let profile = require!(response.profile); + let ciphers = require!(response.ciphers); fn try_into_iter(iter: In) -> Result where @@ -99,13 +96,13 @@ impl SyncResponse { } Ok(SyncResponse { - profile: ProfileResponse::process_response(profile, enc)?, - folders: try_into_iter(response.folders.ok_or(Error::MissingFields)?)?, - collections: try_into_iter(response.collections.ok_or(Error::MissingFields)?)?, + profile: ProfileResponse::process_response(*profile, enc)?, + folders: try_into_iter(require!(response.folders))?, + collections: try_into_iter(require!(response.collections))?, ciphers: try_into_iter(ciphers)?, domains: response.domains.map(|d| (*d).try_into()).transpose()?, - policies: try_into_iter(response.policies.ok_or(Error::MissingFields)?)?, - sends: try_into_iter(response.sends.ok_or(Error::MissingFields)?)?, + policies: try_into_iter(require!(response.policies))?, + sends: try_into_iter(require!(response.sends))?, }) } } @@ -115,7 +112,7 @@ impl ProfileOrganizationResponse { response: ProfileOrganizationResponseModel, ) -> Result { Ok(ProfileOrganizationResponse { - id: response.id.ok_or(Error::MissingFields)?, + id: require!(response.id), }) } } @@ -126,9 +123,9 @@ impl ProfileResponse { _enc: &EncryptionSettings, ) -> Result { Ok(ProfileResponse { - id: response.id.ok_or(Error::MissingFields)?, - name: response.name.ok_or(Error::MissingFields)?, - email: response.email.ok_or(Error::MissingFields)?, + id: require!(response.id), + name: require!(response.name), + email: require!(response.email), //key: response.key, //private_key: response.private_key, organizations: response diff --git a/crates/bitwarden/src/secrets_manager/projects/delete.rs b/crates/bitwarden/src/secrets_manager/projects/delete.rs index 55f792669..05c808c7e 100644 --- a/crates/bitwarden/src/secrets_manager/projects/delete.rs +++ b/crates/bitwarden/src/secrets_manager/projects/delete.rs @@ -7,7 +7,7 @@ use uuid::Uuid; use crate::{ client::Client, - error::{Error, Result}, + error::{require, Result}, }; #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -62,7 +62,7 @@ impl ProjectDeleteResponse { response: BulkDeleteResponseModel, ) -> Result { Ok(ProjectDeleteResponse { - id: response.id.ok_or(Error::MissingFields)?, + id: require!(response.id), error: response.error, }) } diff --git a/crates/bitwarden/src/secrets_manager/projects/project_response.rs b/crates/bitwarden/src/secrets_manager/projects/project_response.rs index 1e6f6a158..af67df9ab 100644 --- a/crates/bitwarden/src/secrets_manager/projects/project_response.rs +++ b/crates/bitwarden/src/secrets_manager/projects/project_response.rs @@ -7,7 +7,7 @@ use uuid::Uuid; use crate::{ client::encryption_settings::EncryptionSettings, - error::{Error, Result}, + error::{require, Result}, }; #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -25,27 +25,19 @@ impl ProjectResponse { response: ProjectResponseModel, enc: &EncryptionSettings, ) -> Result { - let organization_id = response.organization_id.ok_or(Error::MissingFields)?; + let organization_id = require!(response.organization_id); - let name = response - .name - .ok_or(Error::MissingFields)? + let name = require!(response.name) .parse::()? .decrypt(enc, &Some(organization_id))?; Ok(ProjectResponse { - id: response.id.ok_or(Error::MissingFields)?, + id: require!(response.id), organization_id, name, - creation_date: response - .creation_date - .ok_or(Error::MissingFields)? - .parse()?, - revision_date: response - .revision_date - .ok_or(Error::MissingFields)? - .parse()?, + creation_date: require!(response.creation_date).parse()?, + revision_date: require!(response.revision_date).parse()?, }) } } diff --git a/crates/bitwarden/src/secrets_manager/secrets/delete.rs b/crates/bitwarden/src/secrets_manager/secrets/delete.rs index 19337e7a1..f3fe264e1 100644 --- a/crates/bitwarden/src/secrets_manager/secrets/delete.rs +++ b/crates/bitwarden/src/secrets_manager/secrets/delete.rs @@ -7,7 +7,7 @@ use uuid::Uuid; use crate::{ client::Client, - error::{Error, Result}, + error::{require, Result}, }; #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -62,7 +62,7 @@ impl SecretDeleteResponse { response: BulkDeleteResponseModel, ) -> Result { Ok(SecretDeleteResponse { - id: response.id.ok_or(Error::MissingFields)?, + id: require!(response.id), error: response.error, }) } diff --git a/crates/bitwarden/src/secrets_manager/secrets/list.rs b/crates/bitwarden/src/secrets_manager/secrets/list.rs index 7fa46d164..48a507667 100644 --- a/crates/bitwarden/src/secrets_manager/secrets/list.rs +++ b/crates/bitwarden/src/secrets_manager/secrets/list.rs @@ -8,7 +8,7 @@ use uuid::Uuid; use crate::{ client::{encryption_settings::EncryptionSettings, Client}, - error::{Error, Result}, + error::{require, Result}, }; #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -93,16 +93,14 @@ impl SecretIdentifierResponse { response: SecretsWithProjectsInnerSecret, enc: &EncryptionSettings, ) -> Result { - let organization_id = response.organization_id.ok_or(Error::MissingFields)?; + let organization_id = require!(response.organization_id); - let key = response - .key - .ok_or(Error::MissingFields)? + let key = require!(response.key) .parse::()? .decrypt(enc, &Some(organization_id))?; Ok(SecretIdentifierResponse { - id: response.id.ok_or(Error::MissingFields)?, + id: require!(response.id), organization_id, key, }) diff --git a/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs b/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs index fe1a4d342..ffaa7f7e0 100644 --- a/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs +++ b/crates/bitwarden/src/secrets_manager/secrets/secret_response.rs @@ -9,7 +9,7 @@ use uuid::Uuid; use crate::{ client::encryption_settings::EncryptionSettings, - error::{Error, Result}, + error::{require, Result}, }; #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -51,19 +51,13 @@ impl SecretResponse { ) -> Result { let org_id = response.organization_id; - let key = response - .key - .ok_or(Error::MissingFields)? + let key = require!(response.key) .parse::()? .decrypt(enc, &org_id)?; - let value = response - .value - .ok_or(Error::MissingFields)? + let value = require!(response.value) .parse::()? .decrypt(enc, &org_id)?; - let note = response - .note - .ok_or(Error::MissingFields)? + let note = require!(response.note) .parse::()? .decrypt(enc, &org_id)?; @@ -73,21 +67,15 @@ impl SecretResponse { .and_then(|p| p.id); Ok(SecretResponse { - id: response.id.ok_or(Error::MissingFields)?, - organization_id: org_id.ok_or(Error::MissingFields)?, + id: require!(response.id), + organization_id: require!(org_id), project_id: project, key, value, note, - creation_date: response - .creation_date - .ok_or(Error::MissingFields)? - .parse()?, - revision_date: response - .revision_date - .ok_or(Error::MissingFields)? - .parse()?, + creation_date: require!(response.creation_date).parse()?, + revision_date: require!(response.revision_date).parse()?, }) } } diff --git a/crates/bitwarden/src/secrets_manager/state.rs b/crates/bitwarden/src/secrets_manager/state.rs index b2b6f6a8e..c677ab7e9 100644 --- a/crates/bitwarden/src/secrets_manager/state.rs +++ b/crates/bitwarden/src/secrets_manager/state.rs @@ -1,6 +1,6 @@ use std::{fmt::Debug, path::Path}; -use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable}; +use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable, SensitiveString}; use serde::{Deserialize, Serialize}; use crate::{ @@ -15,11 +15,11 @@ const STATE_VERSION: u32 = 1; pub struct ClientState { pub(crate) version: u32, pub(crate) token: String, - pub(crate) encryption_key: String, + pub(crate) encryption_key: SensitiveString, } impl ClientState { - pub fn new(token: String, encryption_key: String) -> Self { + pub fn new(token: String, encryption_key: SensitiveString) -> Self { Self { version: STATE_VERSION, token, diff --git a/crates/bitwarden/src/tool/exporters/mod.rs b/crates/bitwarden/src/tool/exporters/mod.rs index 45bdfd3fd..2da6ac833 100644 --- a/crates/bitwarden/src/tool/exporters/mod.rs +++ b/crates/bitwarden/src/tool/exporters/mod.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use crate::{ client::{LoginMethod, UserLoginMethod}, - error::{Error, Result}, + error::{require, Error, Result}, vault::{ login::LoginUriView, Cipher, CipherType, CipherView, Collection, FieldView, Folder, FolderView, SecureNoteType, @@ -83,7 +83,7 @@ impl TryFrom for bitwarden_exporters::Folder { fn try_from(value: FolderView) -> Result { Ok(Self { - id: value.id.ok_or(Error::MissingFields)?, + id: require!(value.id), name: value.name, }) } @@ -95,7 +95,7 @@ impl TryFrom for bitwarden_exporters::Cipher { fn try_from(value: CipherView) -> Result { let r = match value.r#type { CipherType::Login => { - let l = value.login.ok_or(Error::MissingFields)?; + let l = require!(value.login); bitwarden_exporters::CipherType::Login(Box::new(bitwarden_exporters::Login { username: l.username, password: l.password, @@ -118,7 +118,7 @@ impl TryFrom for bitwarden_exporters::Cipher { }, )), CipherType::Card => { - let c = value.card.ok_or(Error::MissingFields)?; + let c = require!(value.card); bitwarden_exporters::CipherType::Card(Box::new(bitwarden_exporters::Card { cardholder_name: c.cardholder_name, exp_month: c.exp_month, @@ -129,7 +129,7 @@ impl TryFrom for bitwarden_exporters::Cipher { })) } CipherType::Identity => { - let i = value.identity.ok_or(Error::MissingFields)?; + let i = require!(value.identity); bitwarden_exporters::CipherType::Identity(Box::new(bitwarden_exporters::Identity { title: i.title, first_name: i.first_name, @@ -154,7 +154,7 @@ impl TryFrom for bitwarden_exporters::Cipher { }; Ok(Self { - id: value.id.ok_or(Error::MissingFields)?, + id: require!(value.id), folder_id: value.folder_id, name: value.name, notes: value.notes, diff --git a/crates/bitwarden/src/uniffi_support.rs b/crates/bitwarden/src/uniffi_support.rs index b23a9cbef..bf1eade70 100644 --- a/crates/bitwarden/src/uniffi_support.rs +++ b/crates/bitwarden/src/uniffi_support.rs @@ -1,6 +1,6 @@ use std::num::NonZeroU32; -use bitwarden_crypto::{AsymmetricEncString, EncString}; +use bitwarden_crypto::{AsymmetricEncString, EncString, SensitiveString}; use uuid::Uuid; use crate::UniffiCustomTypeConverter; @@ -12,6 +12,11 @@ uniffi::ffi_converter_forward!( bitwarden_crypto::UniFfiTag, crate::UniFfiTag ); +uniffi::ffi_converter_forward!( + SensitiveString, + bitwarden_crypto::UniFfiTag, + crate::UniFfiTag +); type DateTime = chrono::DateTime; uniffi::custom_type!(DateTime, std::time::SystemTime); diff --git a/crates/bitwarden/src/util.rs b/crates/bitwarden/src/util.rs index f6568986d..5611b4077 100644 --- a/crates/bitwarden/src/util.rs +++ b/crates/bitwarden/src/util.rs @@ -6,19 +6,19 @@ use base64::{ }; pub fn default_pbkdf2_iterations() -> NonZeroU32 { - NonZeroU32::new(600_000).unwrap() + NonZeroU32::new(600_000).expect("Non-zero number") } #[cfg(feature = "internal")] pub fn default_argon2_iterations() -> NonZeroU32 { - NonZeroU32::new(3).unwrap() + NonZeroU32::new(3).expect("Non-zero number") } #[cfg(feature = "internal")] pub fn default_argon2_memory() -> NonZeroU32 { - NonZeroU32::new(64).unwrap() + NonZeroU32::new(64).expect("Non-zero number") } #[cfg(feature = "internal")] pub fn default_argon2_parallelism() -> NonZeroU32 { - NonZeroU32::new(4).unwrap() + NonZeroU32::new(4).expect("Non-zero number") } const INDIFFERENT: GeneralPurposeConfig = diff --git a/crates/bitwarden/src/vault/cipher/attachment.rs b/crates/bitwarden/src/vault/cipher/attachment.rs index 1ec6be6fe..a573e92b0 100644 --- a/crates/bitwarden/src/vault/cipher/attachment.rs +++ b/crates/bitwarden/src/vault/cipher/attachment.rs @@ -66,7 +66,12 @@ impl<'a> KeyEncryptable for Attachm // with it, and then encrypt the key with the cipher key let attachment_key = SymmetricCryptoKey::generate(rand::thread_rng()); let encrypted_contents = self.contents.encrypt_with_key(&attachment_key)?; - attachment.key = Some(attachment_key.to_vec().encrypt_with_key(ciphers_key)?); + attachment.key = Some( + attachment_key + .to_vec() + .expose() + .encrypt_with_key(ciphers_key)?, + ); Ok(AttachmentEncryptResult { attachment: attachment.encrypt_with_key(ciphers_key)?, @@ -136,7 +141,7 @@ impl TryFrom for Attachment #[cfg(test)] mod tests { use base64::{engine::general_purpose::STANDARD, Engine}; - use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey}; + use bitwarden_crypto::{EncString, KeyDecryptable, SensitiveString, SymmetricCryptoKey}; use crate::vault::{ cipher::cipher::{CipherRepromptType, CipherType}, @@ -145,7 +150,7 @@ mod tests { #[test] fn test_attachment_key() { - let user_key : SymmetricCryptoKey = "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".parse().unwrap(); + let user_key : SymmetricCryptoKey = SensitiveString::test("w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==").try_into().unwrap(); let attachment = Attachment { id: None, diff --git a/crates/bitwarden/src/vault/cipher/cipher.rs b/crates/bitwarden/src/vault/cipher/cipher.rs index 47b58694d..fa85b5969 100644 --- a/crates/bitwarden/src/vault/cipher/cipher.rs +++ b/crates/bitwarden/src/vault/cipher/cipher.rs @@ -15,7 +15,7 @@ use super::{ login, secure_note, }; use crate::{ - error::{Error, Result}, + error::{require, Error, Result}, vault::password_history, }; @@ -139,10 +139,15 @@ pub struct CipherListView { } impl KeyEncryptable for CipherView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { + fn encrypt_with_key(mut self, key: &SymmetricCryptoKey) -> Result { let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; let key = ciphers_key.as_ref().unwrap_or(key); + // For compatibility reasons, we only create checksums for ciphers that have a key + if ciphers_key.is_some() { + self.generate_checksums(); + } + Ok(Cipher { id: self.id, organization_id: self.organization_id, @@ -177,7 +182,7 @@ impl KeyDecryptable for Cipher { let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; let key = ciphers_key.as_ref().unwrap_or(key); - Ok(CipherView { + let mut cipher = CipherView { id: self.id, organization_id: self.organization_id, folder_id: self.folder_id, @@ -202,7 +207,14 @@ impl KeyDecryptable for Cipher { creation_date: self.creation_date, deleted_date: self.deleted_date, revision_date: self.revision_date, - }) + }; + + // For compatibility we only remove URLs with invalid checksums if the cipher has a key + if ciphers_key.is_some() { + cipher.remove_invalid_checksums(); + } + + Ok(cipher) } } @@ -296,9 +308,23 @@ impl CipherView { let new_key = SymmetricCryptoKey::generate(rand::thread_rng()); - self.key = Some(new_key.to_vec().encrypt_with_key(key)?); + self.key = Some(new_key.to_vec().expose().encrypt_with_key(key)?); Ok(()) } + + pub fn generate_checksums(&mut self) { + if let Some(uris) = self.login.as_mut().and_then(|l| l.uris.as_mut()) { + for uri in uris { + uri.generate_checksum(); + } + } + } + + pub fn remove_invalid_checksums(&mut self) { + if let Some(uris) = self.login.as_mut().and_then(|l| l.uris.as_mut()) { + uris.retain(|u| u.is_checksum_valid()); + } + } } impl KeyDecryptable for Cipher { @@ -358,9 +384,9 @@ impl TryFrom for Cipher { organization_id: cipher.organization_id, folder_id: cipher.folder_id, collection_ids: cipher.collection_ids.unwrap_or_default(), - name: EncString::try_from_optional(cipher.name)?.ok_or(Error::MissingFields)?, + name: require!(EncString::try_from_optional(cipher.name)?), notes: EncString::try_from_optional(cipher.notes)?, - r#type: cipher.r#type.ok_or(Error::MissingFields)?.into(), + r#type: require!(cipher.r#type).into(), login: cipher.login.map(|l| (*l).try_into()).transpose()?, identity: cipher.identity.map(|i| (*i).try_into()).transpose()?, card: cipher.card.map(|c| (*c).try_into()).transpose()?, @@ -386,9 +412,9 @@ impl TryFrom for Cipher { .password_history .map(|p| p.into_iter().map(|p| p.try_into()).collect()) .transpose()?, - creation_date: cipher.creation_date.ok_or(Error::MissingFields)?.parse()?, + creation_date: require!(cipher.creation_date).parse()?, deleted_date: cipher.deleted_date.map(|d| d.parse()).transpose()?, - revision_date: cipher.revision_date.ok_or(Error::MissingFields)?.parse()?, + revision_date: require!(cipher.revision_date).parse()?, key: EncString::try_from_optional(cipher.key)?, }) } diff --git a/crates/bitwarden/src/vault/cipher/field.rs b/crates/bitwarden/src/vault/cipher/field.rs index 25a318d6f..1f18a9537 100644 --- a/crates/bitwarden/src/vault/cipher/field.rs +++ b/crates/bitwarden/src/vault/cipher/field.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; use super::linked_id::LinkedIdType; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, JsonSchema)] #[repr(u8)] @@ -70,7 +70,7 @@ impl TryFrom for Field { Ok(Self { name: EncString::try_from_optional(model.name)?, value: EncString::try_from_optional(model.value)?, - r#type: model.r#type.map(|t| t.into()).ok_or(Error::MissingFields)?, + r#type: require!(model.r#type).into(), linked_id: model .linked_id .map(|id| (id as u32).try_into()) diff --git a/crates/bitwarden/src/vault/cipher/linked_id.rs b/crates/bitwarden/src/vault/cipher/linked_id.rs index 77429438e..b52d756c6 100644 --- a/crates/bitwarden/src/vault/cipher/linked_id.rs +++ b/crates/bitwarden/src/vault/cipher/linked_id.rs @@ -112,7 +112,7 @@ impl TryFrom for LinkedIdType { 416 => Ok(LinkedIdType::Identity(IdentityLinkedIdType::FirstName)), 417 => Ok(LinkedIdType::Identity(IdentityLinkedIdType::LastName)), 418 => Ok(LinkedIdType::Identity(IdentityLinkedIdType::FullName)), - _ => Err(Error::MissingFields), + _ => Err(Error::MissingFields("LinkedIdType")), } } } diff --git a/crates/bitwarden/src/vault/cipher/login.rs b/crates/bitwarden/src/vault/cipher/login.rs index 5d00c41e1..aed691d9a 100644 --- a/crates/bitwarden/src/vault/cipher/login.rs +++ b/crates/bitwarden/src/vault/cipher/login.rs @@ -1,3 +1,4 @@ +use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_api_api::models::{CipherLoginModel, CipherLoginUriModel}; use bitwarden_crypto::{ CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, @@ -7,7 +8,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, JsonSchema)] #[repr(u8)] @@ -28,6 +29,7 @@ pub enum UriMatchType { pub struct LoginUri { pub uri: Option, pub r#match: Option, + pub uri_checksum: Option, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -36,6 +38,35 @@ pub struct LoginUri { pub struct LoginUriView { pub uri: Option, pub r#match: Option, + pub uri_checksum: Option, +} + +impl LoginUriView { + pub(crate) fn is_checksum_valid(&self) -> bool { + let Some(uri) = &self.uri else { + return false; + }; + let Some(cs) = &self.uri_checksum else { + return false; + }; + let Ok(cs) = STANDARD.decode(cs) else { + return false; + }; + + use sha2::Digest; + let uri_hash = sha2::Sha256::new().chain_update(uri.as_bytes()).finalize(); + + uri_hash.as_slice() == cs + } + + pub(crate) fn generate_checksum(&mut self) { + if let Some(uri) = &self.uri { + use sha2::Digest; + let uri_hash = sha2::Sha256::new().chain_update(uri.as_bytes()).finalize(); + let uri_hash = STANDARD.encode(uri_hash.as_slice()); + self.uri_checksum = Some(uri_hash); + } + } } #[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)] @@ -93,6 +124,7 @@ impl KeyEncryptable for LoginUriView { Ok(LoginUri { uri: self.uri.encrypt_with_key(key)?, r#match: self.r#match, + uri_checksum: self.uri_checksum.encrypt_with_key(key)?, }) } } @@ -116,6 +148,7 @@ impl KeyDecryptable for LoginUri { Ok(LoginUriView { uri: self.uri.decrypt_with_key(key)?, r#match: self.r#match, + uri_checksum: self.uri_checksum.decrypt_with_key(key)?, }) } } @@ -166,6 +199,7 @@ impl TryFrom for LoginUri { Ok(Self { uri: EncString::try_from_optional(uri.uri)?, r#match: uri.r#match.map(|m| m.into()), + uri_checksum: EncString::try_from_optional(uri.uri_checksum)?, }) } } @@ -188,23 +222,72 @@ impl TryFrom for Fido2Cre fn try_from(value: bitwarden_api_api::models::CipherFido2CredentialModel) -> Result { Ok(Self { - credential_id: value.credential_id.ok_or(Error::MissingFields)?.parse()?, - key_type: value.key_type.ok_or(Error::MissingFields)?.parse()?, - key_algorithm: value.key_algorithm.ok_or(Error::MissingFields)?.parse()?, - key_curve: value.key_curve.ok_or(Error::MissingFields)?.parse()?, - key_value: value.key_value.ok_or(Error::MissingFields)?.parse()?, - rp_id: value.rp_id.ok_or(Error::MissingFields)?.parse()?, + credential_id: require!(value.credential_id).parse()?, + key_type: require!(value.key_type).parse()?, + key_algorithm: require!(value.key_algorithm).parse()?, + key_curve: require!(value.key_curve).parse()?, + key_value: require!(value.key_value).parse()?, + rp_id: require!(value.rp_id).parse()?, user_handle: EncString::try_from_optional(value.user_handle) .ok() .flatten(), user_name: EncString::try_from_optional(value.user_name).ok().flatten(), - counter: value.counter.ok_or(Error::MissingFields)?.parse()?, + counter: require!(value.counter).parse()?, rp_name: EncString::try_from_optional(value.rp_name).ok().flatten(), user_display_name: EncString::try_from_optional(value.user_display_name) .ok() .flatten(), - discoverable: value.discoverable.ok_or(Error::MissingFields)?.parse()?, - creation_date: value.creation_date.parse().unwrap(), + discoverable: require!(value.discoverable).parse()?, + creation_date: value.creation_date.parse()?, }) } } + +#[cfg(test)] +mod tests { + #[test] + fn test_valid_checksum() { + let uri = super::LoginUriView { + uri: Some("https://example.com".to_string()), + r#match: Some(super::UriMatchType::Domain), + uri_checksum: Some("EAaArVRs5qV39C9S3zO0z9ynVoWeZkuNfeMpsVDQnOk=".to_string()), + }; + assert!(uri.is_checksum_valid()); + } + + #[test] + fn test_invalid_checksum() { + let uri = super::LoginUriView { + uri: Some("https://example.com".to_string()), + r#match: Some(super::UriMatchType::Domain), + uri_checksum: Some("UtSgIv8LYfEdOu7yqjF7qXWhmouYGYC8RSr7/ryZg5Q=".to_string()), + }; + assert!(!uri.is_checksum_valid()); + } + + #[test] + fn test_missing_checksum() { + let uri = super::LoginUriView { + uri: Some("https://example.com".to_string()), + r#match: Some(super::UriMatchType::Domain), + uri_checksum: None, + }; + assert!(!uri.is_checksum_valid()); + } + + #[test] + fn test_generate_checksum() { + let mut uri = super::LoginUriView { + uri: Some("https://test.com".to_string()), + r#match: Some(super::UriMatchType::Domain), + uri_checksum: None, + }; + + uri.generate_checksum(); + + assert_eq!( + uri.uri_checksum.unwrap().as_str(), + "OWk2vQvwYD1nhLZdA+ltrpBWbDa2JmHyjUEWxRZSS8w=" + ); + } +} diff --git a/crates/bitwarden/src/vault/cipher/secure_note.rs b/crates/bitwarden/src/vault/cipher/secure_note.rs index eba4ea2fb..ebcbc3fa4 100644 --- a/crates/bitwarden/src/vault/cipher/secure_note.rs +++ b/crates/bitwarden/src/vault/cipher/secure_note.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, JsonSchema)] #[repr(u8)] @@ -48,7 +48,7 @@ impl TryFrom for SecureNote { fn try_from(model: CipherSecureNoteModel) -> Result { Ok(Self { - r#type: model.r#type.map(|t| t.into()).ok_or(Error::MissingFields)?, + r#type: require!(model.r#type).into(), }) } } diff --git a/crates/bitwarden/src/vault/collection.rs b/crates/bitwarden/src/vault/collection.rs index d20e5d729..ce881cb98 100644 --- a/crates/bitwarden/src/vault/collection.rs +++ b/crates/bitwarden/src/vault/collection.rs @@ -6,7 +6,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -66,8 +66,8 @@ impl TryFrom for Collection { fn try_from(collection: CollectionDetailsResponseModel) -> Result { Ok(Collection { id: collection.id, - organization_id: collection.organization_id.ok_or(Error::MissingFields)?, - name: collection.name.ok_or(Error::MissingFields)?.parse()?, + organization_id: require!(collection.organization_id), + name: require!(collection.name).parse()?, external_id: collection.external_id, hide_passwords: collection.hide_passwords.unwrap_or(false), read_only: collection.read_only.unwrap_or(false), diff --git a/crates/bitwarden/src/vault/folder.rs b/crates/bitwarden/src/vault/folder.rs index edd1cac42..65b18e54b 100644 --- a/crates/bitwarden/src/vault/folder.rs +++ b/crates/bitwarden/src/vault/folder.rs @@ -7,7 +7,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] @@ -55,8 +55,8 @@ impl TryFrom for Folder { fn try_from(folder: FolderResponseModel) -> Result { Ok(Folder { id: folder.id, - name: EncString::try_from_optional(folder.name)?.ok_or(Error::MissingFields)?, - revision_date: folder.revision_date.ok_or(Error::MissingFields)?.parse()?, + name: require!(EncString::try_from_optional(folder.name)?), + revision_date: require!(folder.revision_date).parse()?, }) } } diff --git a/crates/bitwarden/src/vault/send.rs b/crates/bitwarden/src/vault/send.rs index 144933899..37e6efcc9 100644 --- a/crates/bitwarden/src/vault/send.rs +++ b/crates/bitwarden/src/vault/send.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; use uuid::Uuid; -use crate::error::{Error, Result}; +use crate::error::{require, Error, Result}; const SEND_ITERATIONS: u32 = 100_000; @@ -308,19 +308,19 @@ impl TryFrom for Send { Ok(Send { id: send.id, access_id: send.access_id, - name: send.name.ok_or(Error::MissingFields)?.parse()?, + name: require!(send.name).parse()?, notes: EncString::try_from_optional(send.notes)?, - key: send.key.ok_or(Error::MissingFields)?.parse()?, + key: require!(send.key).parse()?, password: send.password, - r#type: send.r#type.ok_or(Error::MissingFields)?.into(), + r#type: require!(send.r#type).into(), file: send.file.map(|f| (*f).try_into()).transpose()?, text: send.text.map(|t| (*t).try_into()).transpose()?, max_access_count: send.max_access_count.map(|s| s as u32), - access_count: send.access_count.ok_or(Error::MissingFields)? as u32, + access_count: require!(send.access_count) as u32, disabled: send.disabled.unwrap_or(false), hide_email: send.hide_email.unwrap_or(false), - revision_date: send.revision_date.ok_or(Error::MissingFields)?.parse()?, - deletion_date: send.deletion_date.ok_or(Error::MissingFields)?.parse()?, + revision_date: require!(send.revision_date).parse()?, + deletion_date: require!(send.deletion_date).parse()?, expiration_date: send.expiration_date.map(|s| s.parse()).transpose()?, }) } @@ -341,7 +341,7 @@ impl TryFrom for SendFile { fn try_from(file: SendFileModel) -> Result { Ok(SendFile { id: file.id, - file_name: file.file_name.ok_or(Error::MissingFields)?.parse()?, + file_name: require!(file.file_name).parse()?, size: file.size.map(|v| v.to_string()), size_name: file.size_name, }) @@ -394,7 +394,7 @@ mod tests { // Get the send key let send_key = Send::get_key(&send_key, k).unwrap(); let send_key_b64 = send_key.to_base64(); - assert_eq!(send_key_b64, "IR9ImHGm6rRuIjiN7csj94bcZR5WYTJj5GtNfx33zm6tJCHUl+QZlpNPba8g2yn70KnOHsAODLcR0um6E3MAlg=="); + assert_eq!(send_key_b64.expose(), "IR9ImHGm6rRuIjiN7csj94bcZR5WYTJj5GtNfx33zm6tJCHUl+QZlpNPba8g2yn70KnOHsAODLcR0um6E3MAlg=="); } fn build_encryption_settings() -> EncryptionSettings { diff --git a/crates/bitwarden/src/vault/totp.rs b/crates/bitwarden/src/vault/totp.rs index 6ba754034..4586eda48 100644 --- a/crates/bitwarden/src/vault/totp.rs +++ b/crates/bitwarden/src/vault/totp.rs @@ -220,8 +220,8 @@ fn decode_b32(s: &str) -> Vec { let mut bytes = Vec::new(); for chunk in bits.as_bytes().chunks_exact(8) { - let byte_str = std::str::from_utf8(chunk).unwrap(); - let byte = u8::from_str_radix(byte_str, 2).unwrap(); + let byte_str = std::str::from_utf8(chunk).expect("The value is a valid string"); + let byte = u8::from_str_radix(byte_str, 2).expect("The value is a valid binary string"); bytes.push(byte); } diff --git a/crates/bw/Cargo.toml b/crates/bw/Cargo.toml index 91f2116e5..421658b0f 100644 --- a/crates/bw/Cargo.toml +++ b/crates/bw/Cargo.toml @@ -25,3 +25,6 @@ tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } [dev-dependencies] tempfile = "3.10.0" + +[lints] +workspace = true diff --git a/crates/bw/src/auth/login.rs b/crates/bw/src/auth/login.rs index e0195f5aa..91e740a3a 100644 --- a/crates/bw/src/auth/login.rs +++ b/crates/bw/src/auth/login.rs @@ -123,17 +123,13 @@ pub(crate) async fn login_device( let email = text_prompt_when_none("Email", email)?; let device_identifier = text_prompt_when_none("Device Identifier", device_identifier)?; - let auth = client - .auth() - .login_device(email, device_identifier) - .await - .unwrap(); + let auth = client.auth().login_device(email, device_identifier).await?; println!("Fingerprint: {}", auth.fingerprint); Text::new("Press enter once approved").prompt()?; - client.auth().login_device_complete(auth).await.unwrap(); + client.auth().login_device_complete(auth).await?; Ok(()) } diff --git a/crates/bws/CHANGELOG.md b/crates/bws/CHANGELOG.md index 2bb431485..642266c70 100644 --- a/crates/bws/CHANGELOG.md +++ b/crates/bws/CHANGELOG.md @@ -7,10 +7,20 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -### Changed +### Added -- Switched TLS backend to `rustls`, removing the dependency on `OpenSSL`. - Add a `BWS_CONFIG_FILE` environment variable to specify the location of the config file (#571) +- The `bws` CLI is now available as a Docker image (`docker run -it bitwarden/bws --help`) (#305) +- The `bws` CLI releases are now code signed on Windows and Mac (#534, #535) + +### Fixed + +- Re-add output options to the help menu after they were accidentally removed (#477) + +### Changed + +- Switched TLS backend to `rusttls`, removing the dependency on `OpenSSL` (#374) +- Updated MSRV for `bws` to `1.71.0` (#589) ## [0.4.0] - 2023-12-21 diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 7ea6fb655..b23ed4edd 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -45,3 +45,6 @@ uuid = { version = "^1.7.0", features = ["serde"] } [dev-dependencies] tempfile = "3.10.0" + +[lints] +workspace = true diff --git a/crates/bws/Dockerfile b/crates/bws/Dockerfile index 4f16f8e6c..cc9e1c481 100644 --- a/crates/bws/Dockerfile +++ b/crates/bws/Dockerfile @@ -15,27 +15,35 @@ COPY . /app # Build project WORKDIR /app/crates/bws -RUN cargo build --release +RUN cargo build --release --bin bws + +# Bundle bws dependencies +RUN mkdir /lib-bws +RUN ldd /app/target/release/bws | tr -s '[:blank:]' '\n' | grep '^/' | xargs -I % cp % /lib-bws + +# Make a HOME directory for the app stage +RUN mkdir -p /home/app ############################################### # App stage # ############################################### -FROM debian:bookworm-slim +FROM scratch ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" +# Set a HOME directory +COPY --from=build /home/app /home/app +ENV HOME=/home/app + # Copy built project from the build stage WORKDIR /usr/local/bin COPY --from=build /app/target/release/bws . -COPY --from=build /etc/ssl/certs /etc/ssl/certs - -# Create a non-root user -RUN useradd -ms /bin/bash app -# Switch to the non-root user -USER app +# Copy certs +COPY --from=build /etc/ssl/certs /etc/ssl/certs -WORKDIR /home/app +# Copy bws dependencies +COPY --from=build /lib-bws /lib ENTRYPOINT ["bws"] diff --git a/crates/bws/src/config.rs b/crates/bws/src/config.rs index 0ea1259f9..6cc471f97 100644 --- a/crates/bws/src/config.rs +++ b/crates/bws/src/config.rs @@ -46,23 +46,28 @@ impl ProfileKey { pub(crate) const FILENAME: &str = "config"; pub(crate) const DIRECTORY: &str = ".bws"; -pub(crate) fn get_config_path(config_file: Option<&Path>, ensure_folder_exists: bool) -> PathBuf { - let config_file = config_file.map(ToOwned::to_owned).unwrap_or_else(|| { - let base_dirs = BaseDirs::new().unwrap(); - base_dirs.home_dir().join(DIRECTORY).join(FILENAME) - }); +fn get_config_path(config_file: Option<&Path>, ensure_folder_exists: bool) -> Result { + let config_file = match config_file { + Some(path) => path.to_owned(), + None => { + let Some(base_dirs) = BaseDirs::new() else { + bail!("A valid home directory doesn't exist"); + }; + base_dirs.home_dir().join(DIRECTORY).join(FILENAME) + } + }; if ensure_folder_exists { if let Some(parent_folder) = config_file.parent() { - std::fs::create_dir_all(parent_folder).unwrap(); + std::fs::create_dir_all(parent_folder)?; } } - config_file + Ok(config_file) } pub(crate) fn load_config(config_file: Option<&Path>, must_exist: bool) -> Result { - let file = get_config_path(config_file, false); + let file = get_config_path(config_file, false)?; let content = match file.exists() { true => read_to_string(file), @@ -75,7 +80,7 @@ pub(crate) fn load_config(config_file: Option<&Path>, must_exist: bool) -> Resul } fn write_config(config: Config, config_file: Option<&Path>) -> Result<()> { - let file = get_config_path(config_file, true); + let file = get_config_path(config_file, true)?; let content = toml::to_string_pretty(&config)?; diff --git a/crates/bws/src/main.rs b/crates/bws/src/main.rs index 6ed78f7b7..182e20679 100644 --- a/crates/bws/src/main.rs +++ b/crates/bws/src/main.rs @@ -328,7 +328,7 @@ async fn process_commands() -> Result<()> { let state_file_path = state::get_state_file_path( profile.and_then(|p| p.state_file_dir).map(Into::into), access_token_obj.access_token_id.to_string(), - ); + )?; let mut client = bitwarden::Client::new(settings); diff --git a/crates/bws/src/render.rs b/crates/bws/src/render.rs index f9563c81b..c92c78662 100644 --- a/crates/bws/src/render.rs +++ b/crates/bws/src/render.rs @@ -41,18 +41,20 @@ pub(crate) fn serialize_response, const N: usiz ) { match output { Output::JSON => { - let mut text = serde_json::to_string_pretty(&data).unwrap(); + let mut text = + serde_json::to_string_pretty(&data).expect("Serialize should be infallible"); // Yaml/table/tsv serializations add a newline at the end, so we do the same here for // consistency text.push('\n'); pretty_print("json", &text, color); } Output::YAML => { - let text = serde_yaml::to_string(&data).unwrap(); + let text = serde_yaml::to_string(&data).expect("Serialize should be infallible"); pretty_print("yaml", &text, color); } Output::Env => { - let valid_key_regex = regex::Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap(); + let valid_key_regex = + regex::Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").expect("regex is valid"); let mut commented_out = false; let mut text: Vec = data @@ -105,7 +107,7 @@ fn pretty_print(language: &str, data: &str, color: bool) { .input_from_bytes(data.as_bytes()) .language(language) .print() - .unwrap(); + .expect("Input is valid"); } else { print!("{}", data); } diff --git a/crates/bws/src/state.rs b/crates/bws/src/state.rs index ef0ef07e2..9c90da81f 100644 --- a/crates/bws/src/state.rs +++ b/crates/bws/src/state.rs @@ -1,18 +1,20 @@ use std::path::PathBuf; +use color_eyre::eyre::Result; + pub(crate) fn get_state_file_path( state_file_dir: Option, access_token_id: String, -) -> Option { +) -> Result> { if let Some(mut state_file_path) = state_file_dir { state_file_path.push(access_token_id); if let Some(parent_folder) = state_file_path.parent() { - std::fs::create_dir_all(parent_folder).unwrap(); + std::fs::create_dir_all(parent_folder)?; } - return Some(state_file_path); + return Ok(Some(state_file_path)); } - None + Ok(None) } diff --git a/crates/memory-testing/src/bin/analyze-dumps.rs b/crates/memory-testing/src/bin/analyze-dumps.rs index fee72f2e5..9957bc1cd 100644 --- a/crates/memory-testing/src/bin/analyze-dumps.rs +++ b/crates/memory-testing/src/bin/analyze-dumps.rs @@ -115,9 +115,7 @@ fn main() -> io::Result<()> { mac_final_pos.is_empty(), ); - // TODO: At the moment we are not zeroizing the base64 key in from_str, so this test is - // ignored - add_row( + error |= add_row( &mut table, format!("Symm. Key in Base64, case {}", idx), &b64_initial_pos, diff --git a/crates/memory-testing/src/main.rs b/crates/memory-testing/src/main.rs index 96e4ae175..ee781683c 100644 --- a/crates/memory-testing/src/main.rs +++ b/crates/memory-testing/src/main.rs @@ -1,6 +1,6 @@ -use std::{env, io::Read, path::Path, process, str::FromStr}; +use std::{env, io::Read, path::Path, process}; -use bitwarden_crypto::SymmetricCryptoKey; +use bitwarden_crypto::{SensitiveString, SymmetricCryptoKey}; fn wait_for_dump() { println!("Waiting for dump..."); @@ -22,8 +22,9 @@ fn main() { let mut symmetric_keys = Vec::new(); let mut symmetric_keys_as_vecs = Vec::new(); - for case in &cases.symmetric_key { - let key = SymmetricCryptoKey::from_str(&case.key).unwrap(); + for case in cases.symmetric_key { + let key = SensitiveString::new(Box::new(case.key)); + let key = SymmetricCryptoKey::try_from(key).unwrap(); symmetric_keys_as_vecs.push(key.to_vec()); symmetric_keys.push(key); } diff --git a/crates/sdk-schemas/Cargo.toml b/crates/sdk-schemas/Cargo.toml index 18ae478f6..3c5e0d50b 100644 --- a/crates/sdk-schemas/Cargo.toml +++ b/crates/sdk-schemas/Cargo.toml @@ -19,7 +19,7 @@ internal = [ ] [dependencies] -anyhow = "1.0.79" +anyhow = "1.0.81" bitwarden = { workspace = true } bitwarden-json = { path = "../bitwarden-json" } bitwarden-uniffi = { path = "../bitwarden-uniffi" } diff --git a/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj b/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj index 3717aefdf..35770eedf 100644 --- a/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj +++ b/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj @@ -16,11 +16,12 @@ Git https://bitwarden.com/products/secrets-manager/ - Bitwarden.Sdk + Bitwarden.Secrets.Sdk bitwarden.png - Bitwarden;Sdk;.NET + Bitwarden;Secrets Manager;Sdk;.NET README.md LICENSE.txt + 0.1.0 diff --git a/languages/js/sdk-client/package-lock.json b/languages/js/sdk-client/package-lock.json index 176ed5eca..dc788c8ce 100644 --- a/languages/js/sdk-client/package-lock.json +++ b/languages/js/sdk-client/package-lock.json @@ -39,9 +39,9 @@ } }, "node_modules/@types/node": { - "version": "18.19.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.18.tgz", - "integrity": "sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg==", + "version": "18.19.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", + "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" diff --git a/languages/php/composer.json b/languages/php/composer.json index 18b333eac..85447e72a 100644 --- a/languages/php/composer.json +++ b/languages/php/composer.json @@ -1,9 +1,10 @@ { - "name": "bitwarden/sdk", + "name": "bitwarden/sdk-secrets", "description": "PHP bindings for interacting with the Bitwarden Secrets Manager. This is a beta release and might be missing some functionality.", "type": "library", "keywords": ["bitwarden","sdk","password-manager"], "homepage": "https://github.com/bitwarden/sdk", + "version": "0.1.0", "require": { "php": "^8.0", "swaggest/json-schema": "^0.12.42", diff --git a/support/build-api.sh b/support/build-api.sh index a668b5564..ea295a523 100755 --- a/support/build-api.sh +++ b/support/build-api.sh @@ -22,5 +22,6 @@ npx openapi-generator-cli generate \ -t ./support/openapi-template \ --additional-properties=packageVersion=1.0.0 +rustup toolchain install nightly cargo +nightly fmt npm run prettier