diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..45af308 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,69 @@ +name: Code Coverage + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + workflow_dispatch: + +jobs: + coverage: + name: Code Coverage + runs-on: ubuntu-latest + + strategy: + matrix: + feature-set: + - "" + - "--features ml-kem" + - "--features post-quantum" + - "--features ml-kem,post-quantum" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin --locked + + - name: Run tests with coverage + run: | + if [ -z "${{ matrix.feature-set }}" ]; then + cargo tarpaulin \ + --out Xml \ + --out Html \ + --output-dir coverage \ + --timeout 300 \ + --fail-under 80 + else + cargo tarpaulin \ + --features ${{ matrix.feature-set }} \ + --out Xml \ + --out Html \ + --output-dir coverage \ + --timeout 300 \ + --fail-under 80 + fi + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: ./coverage/cobertura.xml + flags: ${{ matrix.feature-set || 'default' }} + name: codecov-${{ matrix.feature-set || 'default' }} + fail_ci_if_error: false + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-report-${{ matrix.feature-set || 'default' }} + path: coverage/ + retention-days: 30 + diff --git a/.gitignore b/.gitignore index abf47b8..d1af32c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,14 @@ Thumbs.db # Test artifacts test-results/ +BRANCH_WORK_SUMMARY.md +COMPARISON.md +COVERAGE_ANALYSIS.md +COVERAGE_GAPS_ANALYSIS.md +COVERAGE_IMPROVEMENTS.md +COVERAGE_TESTS_ADDED.md +FIXES.md +IMPLEMENTATION.md +NEXT_STEPS.md +README_COVERAGE.md +src/NOTES.md diff --git a/Cargo.toml b/Cargo.toml index c317adb..fe08064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,35 @@ authors = ["thanos vassilakis"] license = "MIT" description = "Rust implementation of Bottle protocol - layered message containers with encryption and signatures" repository = "https://github.com/thanos/rust-bottle" +readme = "README.md" +keywords = ["cryptography", "encryption", "signature", "bottle", "protocol", "security", "privacy"] +categories = ["cryptography", "encoding", "authentication"] +exclude = [ + "patches/", + "target/", + "coverage/", + "API.md", + "BRANCH_WORK_SUMMARY.md", + "COMPARISON.md", + "COVERAGE_ANALYSIS.md", + "COVERAGE_GAPS_ANALYSIS.md", + "COVERAGE_IMPROVEMENTS.md", + "COVERAGE_TESTS_ADDED.md", + "FIXES.md", + "IMPLEMENTATION.md", + "NEXT_STEPS.md", + "POST_QUANTUM.md", + "PQC_FEATURE_FLAG.md", + "README_COVERAGE.md", + "scripts/", + "tarpaulin.toml", + "build_rs_cov.profraw", + "src/NOTES.md", + ".gitignore", + ".git/", + ".idea/", + ".vscode/", +] [dependencies] # Cryptographic primitives diff --git a/POST_QUANTUM.md b/POST_QUANTUM.md index 8e9cc21..30347cf 100644 --- a/POST_QUANTUM.md +++ b/POST_QUANTUM.md @@ -401,9 +401,9 @@ ml-kem = ["pqcrypto-kyber"] ### macOS/ARM (AArch64) -- **ML-DSA**: ✅ Works (uses clean dilithium2/3/5 implementations) -- **SLH-DSA**: ✅ Works (uses clean sphincsshake256 implementations) -- **ML-KEM**: ❌ Compilation fails due to `pqcrypto-kyber` v0.5 bug +- **ML-DSA**: Works (uses clean dilithium2/3/5 implementations) +- **SLH-DSA**: Works (uses clean sphincsshake256 implementations) +- **ML-KEM**: Compilation fails due to `pqcrypto-kyber` v0.5 bug **Issue**: `pqcrypto-kyber` v0.5.0 has a bug where AVX2 FFI functions are referenced even on AArch64, causing compilation failures. The crate should automatically use the "clean" (generic/portable) implementation on AArch64, but the bug prevents this. @@ -411,15 +411,15 @@ ml-kem = ["pqcrypto-kyber"] ### x86/x86_64 -- **ML-DSA**: ✅ Works -- **SLH-DSA**: ✅ Works -- **ML-KEM**: ✅ Works (uses AVX2-optimized implementation if available) +- **ML-DSA**: Works +- **SLH-DSA**: Works +- **ML-KEM**: Works (uses AVX2-optimized implementation if available) ### Other Platforms -- **ML-DSA**: ✅ Should work (uses clean implementations) -- **SLH-DSA**: ✅ Should work (uses clean implementations) -- **ML-KEM**: ⚠️ May have issues depending on platform +- **ML-DSA**: Should work (uses clean implementations) +- **SLH-DSA**: Should work (uses clean implementations) +- **ML-KEM**: May have issues depending on platform ## Known Limitations diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 630ded1..f9e7a4b 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -4,7 +4,7 @@ set -e -echo "🔍 Running code coverage analysis for rust-bottle" +echo "Running code coverage analysis for rust-bottle" echo "" # Colors for output @@ -14,7 +14,7 @@ NC='\033[0m' # No Color # Check if cargo-tarpaulin is installed if ! command -v cargo-tarpaulin &> /dev/null; then - echo "❌ cargo-tarpaulin is not installed" + echo "ERROR: cargo-tarpaulin is not installed" echo " Install it with: cargo install cargo-tarpaulin" exit 1 fi @@ -35,7 +35,7 @@ run_coverage() { local features=$1 local feature_name=${features:-"default"} - echo -e "${YELLOW}📊 Running coverage with features: ${feature_name}${NC}" + echo -e "${YELLOW}Running coverage with features: ${feature_name}${NC}" if [ -z "$features" ]; then cargo tarpaulin \ @@ -57,10 +57,10 @@ run_coverage() { fi if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ Coverage passed for ${feature_name}${NC}" + echo -e "${GREEN}Coverage passed for ${feature_name}${NC}" echo "" else - echo -e "❌ Coverage failed for ${feature_name}" + echo -e "ERROR: Coverage failed for ${feature_name}" echo "" return 1 fi @@ -75,32 +75,32 @@ for features in "${FEATURES[@]}"; do done # Generate summary -echo "📈 Coverage Summary" +echo "Coverage Summary" echo "==================" echo "" for features in "${FEATURES[@]}"; do feature_name=${features:-"default"} if [ -f "coverage/${feature_name}/cobertura.xml" ]; then - echo "✅ ${feature_name}: Report generated" + echo "SUCCESS: ${feature_name}: Report generated" echo " HTML: coverage/${feature_name}/tarpaulin-report.html" echo " XML: coverage/${feature_name}/cobertura.xml" else - echo "❌ ${feature_name}: No report generated" + echo "ERROR: ${feature_name}: No report generated" fi echo "" done # Open the default coverage report if on macOS if [[ "$OSTYPE" == "darwin"* ]] && [ -f "coverage/default/tarpaulin-report.html" ]; then - echo "🌐 Opening coverage report in browser..." + echo "Opening coverage report in browser..." open "coverage/default/tarpaulin-report.html" fi if [ $FAILED -eq 1 ]; then - echo "⚠️ Some coverage runs failed. Check the output above for details." + echo "WARNING: Some coverage runs failed. Check the output above for details." exit 1 else - echo -e "${GREEN}✅ All coverage reports generated successfully!${NC}" + echo -e "${GREEN}All coverage reports generated successfully!${NC}" exit 0 fi diff --git a/tests/coverage_test.rs b/tests/coverage_test.rs index 5572540..e73cd14 100644 --- a/tests/coverage_test.rs +++ b/tests/coverage_test.rs @@ -2895,16 +2895,13 @@ fn test_decrypt_aes_gcm_success() { // ============================================================================ // Kyber1024 Module Coverage Tests (patches/pqcrypto-kyber-0.5.0/src/kyber1024.rs) // ============================================================================ -// NOTE: These tests are commented out because pqcrypto_kyber is not available as a dependency. -// The kyber1024 module is in the patches directory and may not be directly accessible. -// To enable these tests: -// 1. Add pqcrypto-kyber as a dependency (or path dependency to patches/pqcrypto-kyber-0.5.0) -// 2. Uncomment the tests below +// NOTE: These tests require the "pqcrypto-kyber" feature to be enabled. +// To run these tests: cargo test --features pqcrypto-kyber --test coverage // // Lines to cover: 120-125, 127, 129, 134-139, 141, 157-159, 161-166, 169, 172, // 178-180, 182-186, 188, 191, 206-213, 216, 218, 224-228, 230 -/* +#[cfg(feature = "pqcrypto-kyber")] #[test] fn test_kyber1024_keypair_portable() { // Test lines 120-125, 127, 129: keypair_portable function @@ -2977,6 +2974,7 @@ fn test_kyber1024_decapsulate_portable() { assert_eq!(ss2.as_bytes().len(), kyber1024::shared_secret_bytes()); } +#[cfg(feature = "pqcrypto-kyber")] #[test] fn test_kyber1024_keypair_multiple_times() { // Test to ensure keypair_portable is exercised multiple times @@ -2996,6 +2994,7 @@ fn test_kyber1024_keypair_multiple_times() { } } +#[cfg(feature = "pqcrypto-kyber")] #[test] fn test_kyber1024_encapsulate_decapsulate_round_trip() { // Comprehensive test to exercise all portable functions @@ -3023,6 +3022,7 @@ fn test_kyber1024_encapsulate_decapsulate_round_trip() { } } +#[cfg(feature = "pqcrypto-kyber")] #[test] fn test_kyber1024_from_bytes_error_paths() { // Test error paths in from_bytes (part of the simple_struct macro) @@ -3047,6 +3047,7 @@ fn test_kyber1024_from_bytes_error_paths() { assert!(result4.is_err()); } +#[cfg(feature = "pqcrypto-kyber")] #[test] fn test_kyber1024_from_bytes_success() { // Test successful from_bytes paths @@ -3078,5 +3079,4 @@ fn test_kyber1024_from_bytes_success() { // But we can verify the size is correct assert_eq!(ss.as_bytes().len(), ss_expected.as_bytes().len()); } -*/