Skip to content

Comments

fix: validate SSH private keys for vault encryption#404

Merged
stack72 merged 1 commit intomainfrom
fix/ssh-key-validation-397
Feb 19, 2026
Merged

fix: validate SSH private keys for vault encryption#404
stack72 merged 1 commit intomainfrom
fix/ssh-key-validation-397

Conversation

@keeb
Copy link
Contributor

@keeb keeb commented Feb 19, 2026

Summary

Closes #397

  • Extract binary key material from PEM envelope instead of feeding raw text (including fixed headers/footers/newlines) to PBKDF2, eliminating ~17.5% wasted entropy
  • Reject passphrase-encrypted SSH keys (both OpenSSH and legacy PEM formats) that would silently make vault secrets unrecoverable on passphrase change
  • Validate SSH key file permissions (reject group/other access), matching SSH's own 0600 requirement
  • Refactor duplicated SSH key reading into readAndValidateSshKey() method
  • Bump EncryptedData.version to 2 to mark the new derivation format

Test plan

  • Existing SSH key encryption/decryption tests pass with mode: 0o600
  • New test: reject encrypted OpenSSH key (aes256-ctr cipher detected in binary header)
  • New test: reject encrypted legacy PEM key (Proc-Type: 4,ENCRYPTED header)
  • New test: reject SSH key with insecure permissions (0644)
  • New test: accept SSH key with secure permissions (0600)
  • New test: fall back to auto-generate when SSH key has insecure permissions
  • Full test suite passes (1782 tests, 0 failures)
  • deno check, deno lint, deno fmt all pass

🤖 Generated with Claude Code

Extract binary key material from PEM envelope instead of feeding raw
text (including headers/footers) to PBKDF2. Reject encrypted SSH keys
that would silently break vault secrets on passphrase change. Validate
file permissions match SSH's own 0600 requirement.

Bump EncryptedData.version to 2 for the new derivation format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@keeb keeb force-pushed the fix/ssh-key-validation-397 branch from 7dcf4f0 to 57df9fe Compare February 19, 2026 21:52
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This PR implements important security improvements for SSH key validation in vault encryption. The changes are well-structured, comprehensively tested, and follow the project's code style guidelines.

✅ No Blocking Issues

The PR passes all quality checks:

  • TypeScript strict mode: No any types
  • Named exports: Properly using named exports per CLAUDE.md
  • License headers: Present in both files
  • Test coverage: Comprehensive tests for all new functionality (encrypted key rejection, permission validation, fallback behavior)
  • Security: Addresses multiple security concerns appropriately

DDD Review

The code properly implements a Domain Service pattern. LocalEncryptionVaultProvider encapsulates encryption logic and key management without leaking implementation details. The new validation methods (validateSshKeyPermissions, detectEncryptedPemKey, detectEncryptedOpenSshKey, extractPemKeyMaterial) are appropriately scoped as private helper methods. No DDD anti-patterns detected.

Security Improvements ✅

  1. Binary key extraction - Extracting binary key material from PEM envelope improves entropy usage
  2. Encrypted key rejection - Prevents silent failures when SSH key passphrases change
  3. Permission validation - Matches SSH's own security requirements (0o600)
  4. Windows handling - Sensibly skips POSIX permission checks on Windows

Suggestions (Non-blocking)

  1. Backwards compatibility note: The key derivation change from raw PEM text to extracted binary means v1-encrypted secrets cannot be decrypted with this new code. The version bump to 2 appropriately marks this format change. If there's existing production data using v1 format, consider adding a migration note to the release documentation.

  2. Minor edge case: In readAndValidateSshKey() (line 298), if HOME env var is undefined, the path expansion produces undefined/.ssh/id_rsa. This is an unlikely edge case since HOME is nearly always set, but could be handled with a clearer error message.

  3. Consider allowing 0o400: The permission check rejects anything with (mode & 0o077) !== 0, which correctly blocks group/other access. SSH itself also allows read-only 0o400 permissions, which this code already accepts. Just confirming this is intentional (it is - the check is correct).

Code Quality Highlights

  • Clean refactoring: Extracted duplicated SSH key reading into readAndValidateSshKey()
  • Good error messages with actionable suggestions (e.g., "Run 'chmod 600 ...'")
  • Defensive parsing in detectEncryptedOpenSshKey() - silently allows unknown formats through rather than false-rejecting valid keys
  • Proper test isolation with withTempDir() helper

LGTM! The security improvements are valuable and the implementation is solid.

@stack72 stack72 merged commit 13994ff into main Feb 19, 2026
3 checks passed
@stack72 stack72 deleted the fix/ssh-key-validation-397 branch February 19, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSH private key used as raw PBKDF2 input without validation

2 participants