Implement fLegacy support for BLS signatures - #2
Conversation
This commit adds comprehensive support for the legacy BLS serialization format used by Dash and other systems, enabling interoperability with older implementations. Key changes: - Add legacy serialization traits and implementations for G1/G2 points - Implement format conversion between legacy and modern (IETF) formats - Add legacy-aware secure aggregation (VerifySecure) support - Provide auto-detection for easier migration - Add comprehensive error handling for cross-format issues - Include extensive test coverage for all legacy functionality The implementation maintains full backward compatibility - all existing APIs remain unchanged. Legacy support is opt-in through new methods with '_with_mode' suffixes or explicit legacy parameters. Format differences: - Modern: Compression bit (0x80) set, Y-sign in bit 5 (0x20) - Legacy: Y-sign in bit 7 (0x80), no compression bit This enables agora-blsful to interoperate with Dash blockchain and other systems using the legacy BLS format while maintaining support for the modern IETF standard. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughThis update introduces comprehensive legacy serialization and deserialization support for BLS signatures and keys, enabling compatibility with older formats (notably Dash). It adds traits, implementations, and tests for legacy and modern formats, extends error handling, exposes new mode-aware secure aggregation functions, and updates documentation to describe usage scenarios and code examples for both serialization modes. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant PublicKey
participant Signature
participant SecureAggregation
User->>PublicKey: to_bytes_with_mode(legacy)
User->>PublicKey: from_bytes_with_mode(bytes, legacy)
User->>Signature: to_bytes_with_mode(legacy)
User->>Signature: from_bytes_with_mode(bytes, scheme, legacy)
User->>SecureAggregation: aggregate_secure_with_mode(pubkeys, sigs, legacy)
User->>SecureAggregation: verify_secure_basic_with_mode(pubkeys, agg_sig, msg, legacy)
SecureAggregation->>PublicKey: serialize_with_mode(legacy)
SecureAggregation->>Signature: verify_with_mode(pubkeys, msg, legacy)
Possibly related PRs
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Clippy (1.86.0)warning: failed to write cache, path: /usr/local/registry/index/index.crates.io-1949cf8c6b5b557f/.cache/an/yh/anyhow, error: Permission denied (os error 13) Caused by: ✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Apply consistent code formatting across all files using cargo fmt. This ensures the codebase follows Rust's standard formatting conventions. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (6)
.gitignore (1)
4-4: Consider a recursive pattern for macOS artifacts
.DS_Storealready matches in sub-directories, but some teams prefer the explicit**/.DS_Storefor readability and to avoid confusion about scope. Up to you.tests/c_compatibility_coefficient_test.rs (1)
28-34: Consider extracting common signature extraction logic.The pattern matching to extract
Basicsignatures is repeated. Consider extracting this into a helper function for better maintainability.+ // Helper function to extract Basic signature + fn extract_basic_signature(agg_sig: AggregateSignature<Bls12381G1Impl>) -> Signature<Bls12381G1Impl> { + match agg_sig { + AggregateSignature::Basic(sig) => Signature::Basic(sig), + _ => panic!("Expected Basic aggregate signature"), + } + } + // Test secure aggregation with correct order let secure_agg1 = AggregateSignature::from_signatures_secure(&[sig1, sig2], &[pk1, pk2]).unwrap(); - let secure_sig1_raw = match secure_agg1 { - AggregateSignature::Basic(sig) => sig, - _ => panic!("Expected Basic aggregate signature"), - }; - let secure_final_sig1 = Signature::Basic(secure_sig1_raw); + let secure_final_sig1 = extract_basic_signature(secure_agg1);Also applies to: 42-48
src/public_key.rs (1)
203-208: Validate length before format detection.Consider adding validation that the input has at least the minimum required length before calling the detection logic.
pub fn detect_format(bytes: &[u8]) -> SerializationFormat { - if bytes.len() < 48 { + if bytes.len() != 48 { return SerializationFormat::Unknown; } SerializationFormat::detect_g1(bytes) }tests/legacy_test.rs (2)
74-90: Consider handling the case where no Y=0 key is found within 100 iterations.While it's statistically unlikely, the loop might not find a Y=0 case within 100 iterations. Consider either increasing the iteration limit or using a deterministic approach to generate a Y=0 case.
// For this test, we'll create a legacy format that definitely won't work as modern // by finding a key where legacy Y=0 let mut found_y0 = false; - for i in 0..100 { + for i in 0..1000 { let test_sk = SecretKey::<Bls12381G2Impl>::from_hash(&[i as u8; 32]); let test_pk = test_sk.public_key(); let test_legacy = test_pk.to_bytes_with_mode(true); if (test_legacy[0] & 0x80) == 0 { // Found a key with Y=0 in legacy format // This should definitely fail modern deserialization let result = PublicKey::<Bls12381G2Impl>::from_bytes_with_mode(&test_legacy, false); assert!(result.is_err(), "Legacy Y=0 bytes should not deserialize with modern mode"); found_y0 = true; break; } } assert!(found_y0, "Should find at least one Y=0 case");
246-263: Add assertions for legacy format bit patterns.The test only verifies the modern format compression bit but doesn't assert anything about the legacy format bits. Consider adding assertions to verify the expected legacy format bit patterns.
eprintln!("Modern first byte: 0x{:02x}", modern_bytes[0]); eprintln!("Legacy first byte: 0x{:02x}", legacy_bytes[0]); // Modern format should have bit 7 set (compression) assert!(modern_bytes[0] & 0x80 != 0, "Modern format should have compression bit set"); // Legacy format should not have bit 7 set unless Y=1 // The actual pattern depends on the Y coordinate of this specific point + if (legacy_bytes[0] & 0x80) == 0 { + // Y=0 case: bit 7 should be clear, only lower 5 bits should be used + assert!(legacy_bytes[0] & 0xe0 == 0, "Legacy Y=0 should only use lower 5 bits"); + } else { + // Y=1 case: bit 7 should be set, bits 5-6 should be clear + assert!(legacy_bytes[0] & 0x60 == 0, "Legacy Y=1 should have bits 5-6 clear"); + }src/impls/legacy.rs (1)
98-186: Consider extracting common bit manipulation logic to reduce duplication.The G2 implementation is nearly identical to G1, differing only in array sizes and types. While the current implementation is correct and clear, you could reduce duplication by extracting the bit manipulation logic into generic helper functions.
Example refactor to reduce duplication:
+fn convert_modern_to_legacy_first_byte(byte: u8) -> u8 { + if byte == 0xc0 { return byte; } // Infinity point + let y_sign = (byte & 0x20) != 0; + let mut legacy_byte = byte & 0x1f; + if y_sign { legacy_byte |= 0x80; } + legacy_byte +} + +fn convert_legacy_to_modern_first_byte(byte: u8) -> Result<u8, BlsError> { + if byte == 0xc0 { return Ok(byte); } // Infinity point + let y_sign = (byte & 0x80) != 0; + let cleared = byte & 0x7f; + if cleared & 0xe0 != 0 { + return Err(BlsError::LegacyFormatError( + format!("Invalid legacy format: unexpected bits in byte[0] = 0x{:02x}", byte) + )); + } + let mut modern_byte = cleared | 0x80; + if y_sign { modern_byte |= 0x20; } + Ok(modern_byte) +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (21)
.gitignore(1 hunks)README.md(1 hunks)examples/verify_secure_example.rs(1 hunks)src/aggregate_signature.rs(1 hunks)src/elgamal_ciphertext.rs(2 hunks)src/error.rs(1 hunks)src/impls.rs(1 hunks)src/impls/legacy.rs(1 hunks)src/lib.rs(2 hunks)src/public_key.rs(1 hunks)src/secure_aggregation.rs(1 hunks)src/signature.rs(1 hunks)src/traits.rs(2 hunks)src/traits/legacy_serdes.rs(1 hunks)tests/c_compatibility_coefficient_test.rs(1 hunks)tests/cpp_integration_test.rs(1 hunks)tests/legacy_comprehensive_test.rs(1 hunks)tests/legacy_test.rs(1 hunks)tests/secure_aggregation_test.rs(1 hunks)tests/signatures.rs(3 hunks)tests/utils.rs(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/lib.rs (1)
src/secure_aggregation.rs (7)
aggregate_secure(95-127)aggregate_secure(469-469)aggregate_secure(591-591)aggregate_secure_with_mode(265-304)verify_secure_basic_with_mode(307-345)verify_secure_message_augmentation_with_mode(348-386)verify_secure_pop_with_mode(389-427)
tests/signatures.rs (4)
src/aggregate_signature.rs (2)
from_signatures(126-128)from(110-112)src/multi_signature.rs (2)
from_signatures(147-149)from(112-114)src/traits/sig_multi.rs (1)
from_signatures(7-13)src/multi_public_key.rs (3)
from(42-44)from(50-52)from_public_keys(79-83)
src/public_key.rs (3)
src/signature.rs (4)
to_bytes_with_mode(177-183)from_bytes_with_mode(193-215)detect_format(218-223)to_bytes(186-188)src/impls/legacy.rs (1)
deserialize_g1(41-95)src/traits/legacy_serdes.rs (2)
deserialize_g1(29-29)detect_g1(56-79)
src/impls/legacy.rs (2)
src/traits/legacy_serdes.rs (4)
serialize_g1(26-26)deserialize_g1(29-29)serialize_g2(35-35)deserialize_g2(38-38)src/error.rs (2)
from(58-60)from(64-66)
🔇 Additional comments (38)
src/elgamal_ciphertext.rs (2)
50-56: LGTM: Lifetime parameter simplification.The removal of the explicit
'blifetime parameter is a good improvement. Rust's lifetime elision rules can handle this case automatically since the lifetime isn't used in the return type, making the code cleaner without changing functionality.
66-72: LGTM: Consistent lifetime simplification.Same improvement as above - removing the unnecessary lifetime parameter improves readability while maintaining identical functionality.
src/impls.rs (1)
6-6: Legacy module is internal; no public re-export requiredVerification shows that
src/impls/legacy.rsonly provides implementation details consumed internally (viatraits::legacy_serdes,Signature::to_bytes_with_mode, andPublicKey::to_bytes_with_mode) and isn’t part of the crate’s public API. Omitting apub use legacy::*;is intentional—no further changes needed.src/traits.rs (2)
8-8: LGTM: Consistent module import pattern.The
legacy_serdesmodule import follows the established alphabetical ordering pattern in the file.
24-24: LGTM: Proper public re-export.The public re-export is consistent with the pattern used for all other modules in this file, making legacy serialization traits available throughout the crate.
tests/signatures.rs (7)
110-110: LGTM: Cleaner array passing style.Passing arrays directly instead of slice references is valid since
from_signaturesacceptsAsRef<[Signature<C>]>which arrays implement.
115-115: LGTM: Consistent array passing.Same improvement as above - arrays can be passed directly to
from_signatures.
119-119: LGTM: Consistent array passing for public keys.The
from_public_keysmethod also acceptsAsRef<[PublicKey<C>]>, so arrays can be passed directly.
126-126: LGTM: Consistent array passing.Same valid improvement for error case testing.
146-146: LGTM: Consistent array passing for aggregate signatures.The
AggregateSignature::from_signaturesmethod also acceptsAsRef<[Signature<C>]>.
154-154: LGTM: Consistent array passing.Same valid improvement for the second aggregate signature test.
169-169: LGTM: Consistent array passing.Same valid improvement for the message augmentation test case.
tests/utils.rs (1)
3-5: LGTM: Redundant lifetime annotation removal.Removing the explicit
'staticlifetime annotations is a good simplification. String literals have'staticlifetime by default, making these annotations redundant. The functionality remains identical while improving code readability.README.md (2)
123-123: Excellent security warning about mode consistency.This is a critical security note that will help prevent user errors. Mixing aggregation and verification modes could lead to verification failures and potential security issues.
96-107: Serialization API methods verified as accurate.The methods shown in the README (
to_bytes_with_mode,from_bytes_with_mode, andfrom_bytes_auto) all exist and match the implementation:
- src/public_key.rs:146:
pub fn to_bytes_with_mode(&self, legacy: bool) -> Vec<u8>- src/public_key.rs:159:
pub fn from_bytes_with_mode(bytes: &[u8], legacy: bool) -> BlsResult<Self>- src/public_key.rs:177:
pub fn from_bytes_auto(bytes: &[u8]) -> BlsResult<Self>- Identical signatures also present in src/signature.rs for the signature APIs.
No changes are needed to the examples.
src/lib.rs (2)
40-40: Module declaration looks good.Adding
secure_aggregationas a public module appropriately exposes the new functionality.
73-80: Re-exported functions verified with matching signatures
All five functions—aggregate_secureand the four*_with_modevariants—are defined insrc/secure_aggregation.rs(lines 95, 265, 307, 348, 389) with the expected generic parameters and return types. Thepub useinsrc/lib.rscorrectly re-exports them; no changes needed.tests/c_compatibility_coefficient_test.rs (1)
8-75: Well-structured test for secure aggregation coefficient validation.The test effectively demonstrates several key properties:
- Deterministic behavior: Uses seeded RNG for reproducible results
- Order independence: Tests both forward and reverse ordering
- Security validation: Confirms normal aggregation fails secure verification
The test logic correctly validates that secure aggregation produces verifiable results while normal aggregation does not pass secure verification.
src/aggregate_signature.rs (2)
146-182: Excellent implementation of secure aggregation method.The method demonstrates several best practices:
- Comprehensive validation: Checks array length matching and non-empty inputs
- Scheme consistency: Validates all signatures use the same scheme
- Proper delegation: Uses the dedicated
aggregate_securefunction for core logic- Appropriate wrapping: Returns the result in the correct signature scheme variant
The error handling covers all relevant failure cases and the logic flow is clear and correct.
171-171:as_raw_value()Method Exists and Returns Correct Type
Confirmed thatpub fn as_raw_value(&self) -> &<C as Pairing>::Signatureis implemented insrc/signature.rsand is used consistently inaggregate_signature.rsand tests. No changes required.src/error.rs (2)
30-51: Well-designed error variants for new functionality.The new error types provide excellent coverage for the legacy serialization and secure aggregation features:
InvalidCoefficient: Handles the rare but critical case of zero coefficients in secure aggregationLegacyFormatError: Captures legacy serialization issues with descriptive messagesCrossFormatError: Provides detailed context about format/mode mismatches with structured fieldsInvalidLength: Gives precise information about expected vs actual byte lengthsThe error messages are clear and the structured data in
CrossFormatErrorandInvalidLengthwill be helpful for debugging.
37-43: Excellent structured error design for cross-format issues.The
CrossFormatErrorvariant with separateformatandmodefields provides clear context for debugging serialization issues. The error message template clearly explains the mismatch.examples/verify_secure_example.rs (1)
1-89: Excellent educational example demonstrating secure aggregation.This example effectively demonstrates the rogue key attack vulnerability and how
VerifySecureprevents it. The code is well-structured and includes clear explanations of the security properties.src/public_key.rs (3)
137-152: Well-designed legacy serialization API.The
to_bytes_with_modemethod provides a clean interface for choosing between legacy and modern formats, with proper delegation to the underlying trait methods.
159-172: Proper input validation and error handling.The length validation correctly enforces the 48-byte requirement for G1 points, and the array conversion with proper error propagation is well-implemented.
177-200: Smart format auto-detection with reasonable fallback strategy.The auto-detection logic prioritizes modern format (current default) while providing legacy fallback. The use of format hints to optimize the detection process is a good approach.
tests/secure_aggregation_test.rs (1)
1-139: Comprehensive test coverage for secure aggregation.The test suite effectively covers all critical aspects:
- Prevention of rogue key attacks
- Verification that normal aggregation fails secure verification (important security property)
- Key order independence
- Deterministic behavior
The tests are well-structured and provide good validation of the secure aggregation functionality.
tests/cpp_integration_test.rs (3)
5-14: Simple and effective signature conversion helper.The helper function cleanly converts raw C++ signature bytes to Rust signatures by prepending the scheme byte. This approach leverages existing deserialization logic effectively.
95-128: Thorough bidirectional compatibility testing.The test validates both key derivation consistency and signature verification between C++ and Rust implementations. The secure aggregation test ensures the new functionality works with cross-implementation keys.
174-195: Important security test ensuring aggregation scheme distinction.This test validates that normal aggregation signatures fail secure verification, which is a crucial security property to prevent mixing aggregation schemes.
src/traits/legacy_serdes.rs (4)
7-21: Well-designed general serialization trait.The
LegacySerializetrait provides a clean interface for types supporting both legacy and modern formats with clear boolean flag semantics.
23-39: Appropriate specialization for curve point serialization.The separate traits for G1 and G2 points with fixed array sizes (48 and 96 bytes respectively) provide type safety and align with BLS signature standards.
88-107: Sensible configuration design with good defaults.The configuration struct provides clear constants for legacy and modern modes, with modern as the safe default. This aligns with the goal of maintaining backward compatibility while encouraging modern usage.
56-79: Detection logic is intentionally conservativeThe
detect_g1implementation always returnsUnknownfor any compressed point (bytes[0] & 0x80 != 0), forcing the fallback “try-both-formats” path. A review of the codebase and existing tests shows no other format‐hint heuristics are applied—this behavior matches the assertions intests/legacy_test.rs(which acceptUnknown | Modernfor modern bytes).If you’d like to strengthen detection (for example, treat all bit-7 set cases as
Modernsince legacy uncompressed points never set that bit), you can adjust the bit-6 branch accordingly. Otherwise, the current logic and test coverage align, and no changes are required.src/signature.rs (1)
143-166: LGTM! Clean implementation of secure verification.The method correctly dispatches to scheme-specific secure aggregation functions, providing protection against rogue key attacks. The pattern matching is exhaustive and each variant calls the appropriate verification function.
src/secure_aggregation.rs (1)
20-92: Excellent implementation of deterministic coefficient generation.The function correctly implements the C++ compatible coefficient generation with proper handling of:
- Deterministic sorting of public keys
- Endianness conversion for cross-platform compatibility
- Zero coefficient checking (though extremely unlikely)
- Clear documentation of the C++ compatibility requirements
The scalar conversion logic is complex but necessary for BLS12-381 field arithmetic.
src/impls/legacy.rs (2)
8-96: G1 legacy serialization implementation looks correct.The bit manipulation logic properly converts between modern and legacy formats:
- Correctly extracts/sets Y-coordinate sign bits at different positions
- Handles infinity point (0xc0) consistently
- Includes comprehensive format validation and error handling
77-83: Good security practice with bit validation.The validation that ensures no unexpected high bits are set in legacy format is excellent. This prevents potential security issues from malformed legacy data.
Also applies to: 167-173
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (6)
src/impls/legacy.rs (1)
265-265: Fix misleading comment about Y-coordinate test case.The comment incorrectly states "Test with a point that will have Y=0 in legacy format". The test is actually checking for points with a negative Y-coordinate sign (bit 7 = 0 in legacy format), not Y=0.
- // Test with a point that will have Y=0 in legacy format + // Test with a point that has negative Y-sign in legacy formattests/legacy_comprehensive_test.rs (1)
171-174: Avoid testing private implementation details.The test directly calls
secure_aggregation::hash_public_keys_with_mode, which is a private function not part of the public API. This violates encapsulation principles.Consider either:
- Testing through the public API that internally uses this function
- Making the function
pub(crate)if it needs direct testing- Moving this test to a unit test within the
secure_aggregationmodulesrc/secure_aggregation.rs (4)
279-283: Align empty input handling with standard version.The legacy version returns an error for empty inputs while the standard
aggregate_securefunction returns identity (line 106). This inconsistency could cause confusion when switching between modes.if public_keys.is_empty() { - return Err(BlsError::InvalidInputs( - "Cannot aggregate zero signatures".to_string(), - )); + return Ok(<C as Pairing>::Signature::identity()); }
316-320: Handle empty inputs consistently with standard version.The standard
verify_secure_with_dstproperly handles empty public keys by checking if the signature is identity, but this legacy version simply returns an error.if public_keys.is_empty() { - return Err(BlsError::InvalidInputs( - "Cannot verify with zero public keys".to_string(), - )); + return if signature.is_identity().into() { + Ok(()) + } else { + Err(BlsError::InvalidSignature) + }; }
359-363: Handle empty inputs consistently with standard version.Same issue as in
verify_secure_basic_with_mode- the empty input handling is inconsistent with the standard version.if public_keys.is_empty() { - return Err(BlsError::InvalidInputs( - "Cannot verify with zero public keys".to_string(), - )); + return if signature.is_identity().into() { + Ok(()) + } else { + Err(BlsError::InvalidSignature) + }; }
402-406: Handle empty inputs consistently with standard version.Same issue as in other legacy verify functions - the empty input handling is inconsistent with the standard version.
if public_keys.is_empty() { - return Err(BlsError::InvalidInputs( - "Cannot verify with zero public keys".to_string(), - )); + return if signature.is_identity().into() { + Ok(()) + } else { + Err(BlsError::InvalidSignature) + }; }
🧹 Nitpick comments (2)
tests/legacy_test.rs (1)
278-279: Consider removing debug output from tests.The
eprintln!statements will produce output during test runs. Consider removing them or using conditional compilation if they're needed for debugging.- eprintln!("Modern first byte: 0x{:02x}", modern_bytes[0]); - eprintln!("Legacy first byte: 0x{:02x}", legacy_bytes[0]); + // Uncomment for debugging: + // eprintln!("Modern first byte: 0x{:02x}", modern_bytes[0]); + // eprintln!("Legacy first byte: 0x{:02x}", legacy_bytes[0]);src/impls/legacy.rs (1)
8-198: Consider reducing code duplication between G1 and G2 implementations.The
serialize_g1/serialize_g2anddeserialize_g1/deserialize_g2implementations are nearly identical, differing only in array sizes (48 vs 96 bytes) and type names. This violates the DRY principle and increases maintenance burden.Consider extracting the common logic into generic helper functions:
fn convert_modern_to_legacy(bytes: &mut [u8]) { if bytes[0] == 0xc0 { return; } let y_sign = (bytes[0] & 0x20) != 0; bytes[0] &= 0x1f; if y_sign { bytes[0] |= 0x80; } } fn convert_legacy_to_modern(bytes: &mut [u8]) -> Result<(), BlsError> { if bytes[0] == 0xc0 { return Ok(()); } let y_sign = (bytes[0] & 0x80) != 0; let first_byte = bytes[0]; bytes[0] &= 0x7f; if bytes[0] & 0xe0 != 0 { return Err(BlsError::LegacyFormatError(format!( "Invalid legacy format: unexpected bits in byte[0] = 0x{:02x}", first_byte ))); } bytes[0] |= 0x80; if y_sign { bytes[0] |= 0x20; } Ok(()) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/impls/legacy.rs(1 hunks)src/lib.rs(2 hunks)src/public_key.rs(1 hunks)src/public_key_share.rs(2 hunks)src/secure_aggregation.rs(1 hunks)src/signature.rs(1 hunks)src/signature_share.rs(2 hunks)src/traits/legacy_serdes.rs(1 hunks)tests/cpp_integration_test.rs(1 hunks)tests/legacy_comprehensive_test.rs(1 hunks)tests/legacy_test.rs(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- src/public_key.rs
- src/signature.rs
🧰 Additional context used
🧬 Code Graph Analysis (4)
tests/legacy_test.rs (6)
tests/signatures.rs (1)
SecretKey(45-45)src/public_key.rs (3)
from_bytes_with_mode(159-172)detect_format(203-208)from_bytes_auto(177-200)src/signature.rs (6)
from_bytes_with_mode(193-215)default(28-30)verify(98-106)detect_format(218-223)as_raw_value(136-142)verify_secure_with_mode(226-246)src/signature_share.rs (3)
default(15-17)verify(100-102)as_raw_value(115-121)src/aggregate_signature.rs (2)
default(28-30)verify(185-194)src/public_key_share.rs (1)
verify(55-71)
tests/cpp_integration_test.rs (3)
src/public_key.rs (3)
try_from(58-74)from(14-16)from(50-52)src/signature.rs (5)
try_from(91-93)try_from(257-289)from(83-85)verify(98-106)verify_secure(145-165)src/aggregate_signature.rs (1)
from_signatures_secure(146-182)
src/impls/legacy.rs (2)
src/traits/legacy_serdes.rs (4)
serialize_g1(25-25)deserialize_g1(28-28)serialize_g2(34-34)deserialize_g2(37-37)src/error.rs (2)
from(58-60)from(64-66)
tests/legacy_comprehensive_test.rs (7)
tests/signatures.rs (1)
SecretKey(45-45)src/public_key.rs (5)
from_bytes_with_mode(159-172)try_from(58-74)shares(129-132)from_shares(128-134)to_bytes(213-215)src/signature.rs (7)
from_bytes_with_mode(193-215)verify(98-106)try_from(91-93)try_from(257-289)shares(123-126)from_shares(119-133)to_bytes(186-188)src/public_key_share.rs (3)
verify(55-71)try_from(46-50)bytes(100-110)src/signature_share.rs (3)
verify(100-102)try_from(86-95)bytes(155-177)src/aggregate_signature.rs (3)
verify(185-194)try_from(83-104)try_from(118-120)src/secure_aggregation.rs (6)
PublicKey(525-525)hash_public_keys_with_mode(197-262)hash_public_keys_with_mode(286-286)hash_public_keys_with_mode(323-323)hash_public_keys_with_mode(366-366)hash_public_keys_with_mode(409-409)
🔇 Additional comments (13)
src/signature_share.rs (1)
131-133: LGTM! Formatting improvement.The multi-line formatting of the identifier construction improves readability.
src/public_key_share.rs (1)
81-83: LGTM! Consistent formatting.The multi-line formatting matches the style used in
signature_share.rs, maintaining consistency across the codebase.tests/cpp_integration_test.rs (5)
5-14: Well-designed helper function.The function correctly handles the conversion from raw C++ signature bytes to Rust's scheme-prefixed format by prepending the Basic scheme byte (0x00).
86-121: Comprehensive cross-compatibility test.The test thoroughly validates:
- C++ key import into Rust
- Secret key to public key derivation
- Individual signature verification
- Secure aggregation and verification
Good test coverage for two-signer scenario.
125-165: Good scalability test.Testing with three signers ensures the secure aggregation works correctly with different participant counts.
169-192: Important security test.This negative test case correctly validates that normal aggregations fail secure verification, ensuring the security properties of secure aggregation are enforced.
195-224: Complete bidirectional compatibility testing.This test validates Rust-to-C++ compatibility, complementing the C++-to-Rust tests above. The use of deterministic key generation ensures reproducible results.
src/traits/legacy_serdes.rs (3)
6-20: Well-designed serialization trait.The trait provides a clean API for types that support both legacy and modern serialization formats. The boolean parameter for mode selection is simple and effective.
53-85: Robust format detection implementation.The format detection logic correctly handles:
- Infinity point (0xc0) as
Eitherformat- Legacy Y=0 cases (bit 7 not set) as
Legacy- Ambiguous cases as
Unknown- Invalid bit patterns
The reuse of G1 logic for G2 is appropriate since both follow the same encoding rules.
87-106: Clean configuration API.The
SerializationConfigstruct provides a simple and intuitive way to specify serialization mode. The convenient constants (LEGACYandMODERN) and sensible default (modern format) make the API user-friendly.tests/legacy_test.rs (3)
5-106: Comprehensive serialization tests.Excellent test coverage for:
- Roundtrip serialization in both formats
- Infinity point special handling
- Cross-format incompatibility validation
- Edge case with legacy Y=0 format
The search for Y=0 cases in lines 87-105 is a clever way to ensure testing of unambiguous legacy format.
143-234: Critical security validation tests.These tests ensure:
- Legacy and modern aggregations use different coefficients
- Cross-mode verification correctly fails
- Secure aggregation works correctly in each mode
The test that coefficients differ (lines 207-234) is particularly important for security.
291-312: Thorough error handling validation.Good coverage of error cases including:
- Invalid point data
- Invalid length inputs
- Proper error enum matching
The validation of specific error fields (expected/actual length) ensures error messages are informative.
5a2c32e to
06701c7
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
tests/legacy_test.rs (3)
87-106: Optimize brute force search for better performance.The current approach searches through 100 iterations to find a Y=0 case, which is inefficient and may be flaky.
Consider using a deterministic approach or caching known test vectors:
- // For this test, we'll create a legacy format that definitely won't work as modern - // by finding a key where legacy Y=0 - let mut found_y0 = false; - for i in 0..100 { - let test_sk = SecretKey::<Bls12381G2Impl>::from_hash(&[i as u8; 32]); - let test_pk = test_sk.public_key(); - let test_legacy = test_pk.to_bytes_with_mode(true); - - if (test_legacy[0] & 0x80) == 0 { - // Found a key with Y=0 in legacy format - // This should definitely fail modern deserialization - let result = PublicKey::<Bls12381G2Impl>::from_bytes_with_mode(&test_legacy, false); - assert!( - result.is_err(), - "Legacy Y=0 bytes should not deserialize with modern mode" - ); - found_y0 = true; - break; - } - } - assert!(found_y0, "Should find at least one Y=0 case"); + // Use a known seed that produces Y=0 (pre-computed for deterministic testing) + let test_sk = SecretKey::<Bls12381G2Impl>::from_hash(&[0x17; 32]); // Known Y=0 case + let test_pk = test_sk.public_key(); + let test_legacy = test_pk.to_bytes_with_mode(true); + + // Verify it's actually Y=0 + assert_eq!(test_legacy[0] & 0x80, 0, "Should be Y=0 case"); + + // This should fail modern deserialization + let result = PublicKey::<Bls12381G2Impl>::from_bytes_with_mode(&test_legacy, false); + assert!( + result.is_err(), + "Legacy Y=0 bytes should not deserialize with modern mode" + );
240-249: Optimize brute force search for deterministic testing.Similar to the earlier brute force search, this approach is inefficient and potentially flaky.
Use a pre-computed test vector instead:
- // Test with a key that has Y=0 in legacy format (unambiguous) - let mut pk_y0 = None; - for i in 0..100u8 { - let test_sk = SecretKey::<Bls12381G2Impl>::from_hash(&[i; 32]); - let test_pk = test_sk.public_key(); - let test_legacy = test_pk.to_bytes_with_mode(true); - - if (test_legacy[0] & 0x80) == 0 { - pk_y0 = Some((test_sk, test_pk)); - break; - } - } - - let (_sk, pk) = pk_y0.expect("Should find Y=0 case"); + // Use known Y=0 case for deterministic testing + let sk = SecretKey::<Bls12381G2Impl>::from_hash(&[0x17; 32]); + let pk = sk.public_key(); + + // Verify this is indeed a Y=0 case + let legacy_check = pk.to_bytes_with_mode(true); + assert_eq!(legacy_check[0] & 0x80, 0, "Should be Y=0 case");
278-279: Remove debug print statements.Debug print statements should not be included in test code unless they're behind a feature flag or conditional compilation.
- eprintln!("Modern first byte: 0x{:02x}", modern_bytes[0]); - eprintln!("Legacy first byte: 0x{:02x}", legacy_bytes[0]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
README.md(1 hunks)src/aggregate_signature.rs(1 hunks)src/error.rs(1 hunks)src/impls.rs(1 hunks)src/impls/legacy.rs(1 hunks)src/lib.rs(2 hunks)src/public_key.rs(1 hunks)src/public_key_share.rs(2 hunks)src/secure_aggregation.rs(1 hunks)src/signature.rs(1 hunks)src/signature_share.rs(2 hunks)src/traits.rs(2 hunks)src/traits/legacy_serdes.rs(1 hunks)tests/cpp_integration_test.rs(5 hunks)tests/legacy_comprehensive_test.rs(1 hunks)tests/legacy_test.rs(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/aggregate_signature.rs
- tests/cpp_integration_test.rs
🚧 Files skipped from review as they are similar to previous changes (12)
- src/impls.rs
- src/public_key_share.rs
- README.md
- src/traits.rs
- src/signature_share.rs
- src/error.rs
- src/lib.rs
- src/impls/legacy.rs
- src/public_key.rs
- src/signature.rs
- src/secure_aggregation.rs
- tests/legacy_comprehensive_test.rs
🔇 Additional comments (10)
src/traits/legacy_serdes.rs (4)
1-20: LGTM! Well-structured trait definitions.The
LegacySerializetrait provides a clean interface for dual-format serialization support. The method signatures are intuitive and properly documented.
22-38: LGTM! Fixed-size array traits are correctly defined.The
LegacyG1PointandLegacyG2Pointtraits appropriately use fixed-size arrays (48 and 96 bytes respectively) which provides compile-time guarantees for the expected serialization sizes.
87-106: LGTM! Clean configuration design.The
SerializationConfigstruct provides a clear API with sensible defaults and convenient constants for common configurations.
80-85: G2 detection delegation is correctThe
detect_g1function inspects only the flag bits in the first byte—which are defined identically for both G1 (48-byte) and G2 (96-byte) compressed encodings—so delegatingdetect_g2todetect_g1is appropriate. The existing tests insrc/impls/legacy.rsalready validate that legacy vs. modern formats round-trip correctly and differ in their first‐byte flags for both curve groups. No changes required.tests/legacy_test.rs (6)
5-23: LGTM! Solid roundtrip testing.The G1 serialization roundtrip test properly validates that both modern and legacy formats work correctly and produce different byte representations.
25-35: LGTM! Important infinity point edge case.Testing that the infinity point serializes identically in both formats is crucial for interoperability.
37-68: LGTM! Comprehensive G2 signature testing.The G2 signature roundtrip test properly validates both formats and confirms they produce different serializations while maintaining verification correctness.
143-205: LGTM! Critical security test for cross-format verification.This test properly validates that signatures aggregated in one mode fail verification in the other mode, which is essential for security.
207-234: LGTM! Important coefficient generation validation.Testing that legacy and modern modes produce different aggregation coefficients is crucial for security - using the wrong mode would compromise the aggregation.
291-312: LGTM! Comprehensive error handling validation.The error message testing properly validates both invalid point data and invalid length scenarios with appropriate error variant matching.
- Fix README.md example to use correct function names and variables - Remove redundant to_bytes method that duplicated to_bytes_with_mode(false) - Refactor test to use public API instead of private hash_public_keys_with_mode - Make legacy verify functions handle empty public_keys consistently with modern - Update legacy aggregate function to return identity for empty input - Correct comment about negative Y-coordinate test in legacy format These changes ensure consistent behavior between legacy and modern modes and improve API clarity by removing redundant methods. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/impls/legacy.rs (1)
268-279: Improve cross-format test reliability.The current approach of iterating through scalar multipliers to find a specific bit pattern may not be deterministic and could potentially fail on different curve implementations or parameter sets.
Consider using a more deterministic approach:
- // Test with a point that has a negative Y-coordinate sign (bit 7 = 0 in legacy format) - // This bit pattern causes the serialized value to fail modern format validation - // due to the incorrect bit prefix - for i in 1..20u64 { - let scalar = <G1Projective as Group>::Scalar::from(i); - let test_point = G1Projective::generator() * scalar; - let test_legacy = test_point.serialize_g1(true); - - if (test_legacy[0] & 0x80) == 0 { - // Found Y=0 case - this should fail modern deserialization - let result = G1Projective::deserialize_g1(&test_legacy, false); - assert!(result.is_err(), "Legacy Y=0 should fail modern deser"); - break; - } - } + // Create a deterministic test case with crafted legacy bytes + let mut test_legacy = [0u8; 48]; + test_legacy[0] = 0x1f; // Set all lower bits but not bit 7 (Y-sign = 0) + // This should fail modern deserialization due to missing compression bit + let result = G1Projective::deserialize_g1(&test_legacy, false); + assert!(result.is_err(), "Legacy format without compression bit should fail modern deserialization");
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
README.md(1 hunks)src/impls/legacy.rs(1 hunks)src/secure_aggregation.rs(1 hunks)src/signature.rs(1 hunks)tests/legacy_comprehensive_test.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- README.md
- src/signature.rs
- src/secure_aggregation.rs
🔇 Additional comments (10)
src/impls/legacy.rs (4)
265-267: Well-addressed comment update.The comment has been properly updated to correctly describe that the test is checking for a point with a negative Y-coordinate sign (bit 7 = 0 in legacy format), not Y=0. This addresses the previous review feedback accurately.
10-39: Solid implementation of legacy G1 serialization.The bit manipulation logic is correct:
- Properly extracts Y-coordinate sign from bit 5 (modern format)
- Correctly clears top 3 bits and sets bit 7 for legacy format
- Handles infinity point as a special case (same in both formats)
The implementation follows the documented format differences between modern and legacy serialization.
41-102: Comprehensive legacy G1 deserialization with proper validation.The implementation correctly:
- Validates modern format bit patterns (bit 7 set, bit 6 clear)
- Converts legacy format by extracting Y-sign from bit 7 and moving to bit 5
- Validates that no unexpected bits are set in legacy format
- Provides detailed error messages for debugging
The error handling is thorough and includes both format validation and point validation.
105-198: Consistent G2 implementation mirrors G1 logic.The G2 implementation correctly duplicates the G1 logic for 96-byte serialization. This consistency is important for maintaining the same format conversion semantics across both curve groups.
tests/legacy_comprehensive_test.rs (6)
192-197: Excellent use of public secure aggregation API.The tests properly use the public
secure_aggregation::aggregate_secure_with_modeandsecure_aggregation::aggregate_securefunctions instead of calling private implementation details. This maintains proper encapsulation and tests the intended public interface.
8-47: Comprehensive signature scheme testing.The test systematically validates all three BLS signature schemes (Basic, MessageAugmentation, ProofOfPossession) with both legacy and modern serialization formats. The cross-format deserialization testing appropriately handles both success and failure cases.
242-288: Robust randomized testing approach.The use of 100 random seeds with deterministic ChaCha20Rng provides excellent coverage for edge cases while maintaining test reproducibility. This approach effectively validates the roundtrip correctness across a wide range of key/signature pairs.
379-402: Thoughtful edge case handling.The test correctly validates that zero scalar is not a valid secret key and properly tests the identity point serialization. The assertion that modern and legacy formats are identical for infinity points is an important invariant to verify.
404-435: Excellent coverage of Y-coordinate cases.This test ensures both Y-coordinate parity cases (bit 7 = 0 and bit 7 = 1 in legacy format) are covered and validates roundtrip correctness for both cases. This is crucial for comprehensive format conversion testing.
211-240: Comprehensive malformed data validation.The test covers multiple malformed input scenarios including invalid lengths, invalid bit patterns, and cross-format validation errors. The use of
matches!macro for specific error type checking is a good practice.
The detect_g1 function now properly handles all serialization formats: - Legacy Y=0: bit 7 clear (0x00-0x7F) - Legacy Y=1: bit 7 set, can overlap with modern (0x80-0xBF when bit 6 clear) - Modern compressed: bit 7 set, bit 6 clear (0x80-0xBF) - Unknown: bit pattern 11xxxxxx (except infinity 0xc0) Since modern compressed and legacy Y=1 formats can have overlapping bit patterns in the 0x80-0xBF range, the function returns Unknown for ambiguous cases. This ensures proper format detection without false negatives. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
The auto-detection feature added unnecessary complexity and potential security risks: - Many byte patterns were ambiguous between modern and legacy formats - Auto-detection could mask format confusion bugs - The API is clearer when format is explicitly specified Changes: - Remove SerializationFormat enum and all detection methods - Remove from_bytes_auto() from PublicKey - Remove detect_format() from PublicKey and Signature - Update tests to remove auto-detection tests - Update README to remove auto-detection examples The explicit API remains: - to_bytes() / from_bytes() for modern format - to_bytes_with_mode(true) / from_bytes_with_mode(bytes, true) for legacy This ensures users always know which format they're working with. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/traits/legacy_serdes.rs (1)
6-38: Consider adding format auto-detection methods.The current traits require explicit
legacyparameters for all operations. Consider adding convenience methods that can auto-detect the format to improve usability during migration scenarios.pub trait LegacySerialize: Sized { // ... existing methods ... /// Deserialize with automatic format detection fn deserialize_auto(bytes: &[u8]) -> Result<Self, crate::BlsError>; } pub trait LegacyG1Point: Sized { // ... existing methods ... /// Deserialize G1 point with automatic format detection fn deserialize_g1_auto(bytes: &[u8; 48]) -> Result<Self, crate::BlsError>; } pub trait LegacyG2Point: Sized { // ... existing methods ... /// Deserialize G2 point with automatic format detection fn deserialize_g2_auto(bytes: &[u8; 96]) -> Result<Self, crate::BlsError>; }This would align with the PR's goal of "auto-detection of format for easier migration" mentioned in the objectives.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
README.md(1 hunks)src/public_key.rs(1 hunks)src/signature.rs(1 hunks)src/traits/legacy_serdes.rs(1 hunks)tests/legacy_test.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- README.md
- src/signature.rs
- src/public_key.rs
- tests/legacy_test.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/traits/legacy_serdes.rs (3)
src/public_key_share.rs (1)
bytes(100-110)src/signature_share.rs (1)
bytes(155-177)src/impls/legacy.rs (4)
serialize_g1(10-39)deserialize_g1(41-101)serialize_g2(106-135)deserialize_g2(137-197)
🔇 Additional comments (4)
src/traits/legacy_serdes.rs (4)
1-5: Documentation is clear and concise.The module documentation effectively explains the purpose and mentions Dash compatibility, which aligns with the PR objectives.
6-20: Well-designed general trait with appropriate flexibility.The
LegacySerializetrait provides a clean API for types supporting both serialization formats. UsingVec<u8>for the return type offers flexibility for variable-length serialization.
22-29: Type-safe G1 point trait with fixed-size arrays.The use of fixed-size arrays
[u8; 48]for G1 points provides excellent type safety and matches the expected 48-byte compressed point size.
31-38: Consistent G2 point trait design.The G2 trait follows the same pattern as G1 with appropriate 96-byte arrays, maintaining consistency across the API.
This refactoring improves code clarity and type safety by replacing all
`bool legacy` parameters with a proper `SerializationFormat` enum.
Changes:
- Add SerializationFormat enum with Legacy and Modern variants
- Update all trait definitions to use SerializationFormat
- Update all implementations to use pattern matching
- Update all function signatures and call sites
- Update tests to use SerializationFormat::{Legacy,Modern}
- Update documentation and README examples
Benefits:
- Type safety: Can't accidentally pass wrong boolean value
- Self-documenting: SerializationFormat::Legacy is clearer than true
- Future extensibility: Easy to add new formats if needed
- Better IDE support with enum autocomplete
All tests pass and the API maintains backward compatibility while
being more maintainable and clearer to use.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
README.md(1 hunks)src/impls/legacy.rs(1 hunks)src/lib.rs(3 hunks)src/public_key.rs(1 hunks)src/secure_aggregation.rs(1 hunks)src/serialization.rs(1 hunks)src/signature.rs(1 hunks)src/traits/legacy_serdes.rs(1 hunks)tests/legacy_comprehensive_test.rs(1 hunks)tests/legacy_test.rs(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/serialization.rs
🚧 Files skipped from review as they are similar to previous changes (6)
- README.md
- src/lib.rs
- src/public_key.rs
- src/signature.rs
- src/impls/legacy.rs
- tests/legacy_comprehensive_test.rs
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/traits/legacy_serdes.rs (3)
src/public_key_share.rs (1)
bytes(100-110)src/signature_share.rs (1)
bytes(155-177)src/impls/legacy.rs (4)
serialize_g1(10-39)deserialize_g1(41-104)serialize_g2(109-138)deserialize_g2(140-203)
tests/legacy_test.rs (7)
src/secure_aggregation.rs (1)
PublicKey(542-542)src/public_key.rs (1)
from_bytes_with_mode(158-171)src/signature.rs (5)
from_bytes_with_mode(187-209)default(28-30)verify(98-106)as_raw_value(136-142)verify_secure_with_mode(213-233)src/serialization.rs (1)
default(24-26)src/signature_share.rs (3)
default(15-17)verify(100-102)as_raw_value(115-121)src/aggregate_signature.rs (2)
default(28-30)verify(185-194)src/impls.rs (1)
default(31-33)
🔇 Additional comments (11)
src/traits/legacy_serdes.rs (1)
1-41: LGTM! Well-designed trait interface for legacy serialization support.The trait definitions provide a clean and consistent interface for legacy serialization. The method signatures are appropriate with proper error handling, and the documentation clearly explains the purpose of each trait.
tests/legacy_test.rs (6)
5-23: LGTM! Solid roundtrip serialization test.The test properly validates that serialization and deserialization work correctly in both formats, and importantly verifies that the formats produce different byte representations.
25-35: LGTM! Important edge case test for infinity points.This test correctly validates that infinity points have the same representation in both legacy and modern formats, which is crucial for compatibility.
70-106: LGTM! Thorough cross-format incompatibility validation.The test properly validates that formats are incompatible by attempting cross-format deserialization and expecting failures. The approach of searching for a Y=0 case is good for ensuring the test covers the legacy-specific bit patterns.
109-171: LGTM! Comprehensive secure aggregation compatibility test.This test validates the critical functionality of secure aggregation with legacy mode, ensuring that:
- Legacy aggregated signatures verify with legacy mode
- Modern aggregated signatures verify with modern mode
- Cross-mode verification fails (important security property)
173-200: LGTM! Important security validation for coefficient generation.This test ensures that coefficient generation differs between legacy and modern modes, which is crucial for security. Different serialization formats must produce different coefficients to maintain the security properties of secure aggregation.
226-246: LGTM! Proper error handling validation.The test appropriately validates error handling for invalid inputs and incorrect lengths, ensuring proper error types are returned.
src/secure_aggregation.rs (4)
277-315: LGTM! Well-implemented legacy-aware aggregation function.The function properly handles legacy serialization for sorting and coefficient application. The empty input handling is consistent with the standard version, which is good.
317-360: LGTM! Consistent legacy-aware verification implementation.The verification function properly implements legacy-aware coefficient generation and public key aggregation. The empty input handling matches the standard version's behavior.
362-405: LGTM! Consistent pattern for MessageAugmentation scheme.The implementation follows the same pattern as the Basic scheme verification with appropriate DST usage.
407-450: LGTM! Consistent pattern for ProofOfPossession scheme.The implementation follows the same pattern as other schemes with appropriate DST usage for the PoP scheme.
- Use derive(Default) for SerializationFormat enum - Add type alias to reduce type complexity in secure_aggregation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Remove platform-dependent byte reversal in hash_public_keys_with_mode to align with the standard hash_public_keys_with_sorted function. This ensures consistent cross-platform behavior by interpreting field representations according to the cryptographic library's specification rather than platform endianness. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/secure_aggregation.rs (2)
368-410: Apply the same sorting optimization.This function has the same redundant sorting issue as the previous verification function.
Apply the same optimization pattern to eliminate redundant sorting as suggested for the other verification functions.
413-455: Apply the same sorting optimization.This function has the same redundant sorting issue as the other verification functions.
Apply the same optimization pattern to eliminate redundant sorting as suggested for the other verification functions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/secure_aggregation.rs(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/secure_aggregation.rs (3)
src/signature.rs (3)
verify_secure_basic_with_mode(224-224)verify_secure_message_augmentation_with_mode(227-227)verify_secure_pop_with_mode(230-230)tests/legacy_test.rs (4)
aggregate_secure_with_mode(135-135)aggregate_secure_with_mode(143-143)aggregate_secure_with_mode(193-193)aggregate_secure_with_mode(196-196)src/traits/sig_core.rs (1)
core_verify(120-146)
🔇 Additional comments (4)
src/secure_aggregation.rs (4)
28-32: Type alias improves code readability.The
SortedKeysWithCoefficientstype alias makes the return type ofhash_public_keys_with_sortedmore readable and maintainable.
332-338: Consistent empty input handling implemented correctly.The empty input handling in all three legacy verification functions now matches the standard version by checking if the signature is identity, which addresses the previous review feedback.
Also applies to: 377-383, 422-428
297-299: Consistent empty input handling for aggregation.The empty input handling in the legacy aggregation function now returns the identity signature, matching the standard version behavior.
261-265: Platform-dependent endianness conversion correctly removed.The removal of platform-dependent endianness conversion aligns with the standard function's approach and addresses the cross-platform compatibility issue mentioned in previous reviews.
Refactored legacy secure aggregation functions to eliminate duplicate sorting: - Added hash_public_keys_with_sorted_mode that returns both sorted keys and coefficients - Updated aggregate_secure_with_mode to sort keys only once - Created verify_secure_with_dst_and_mode to consolidate verify logic - Removed ~90 lines of duplicated code across verify functions - Improved performance by eliminating redundant sorting operations This follows the pattern established by the standard (non-legacy) functions and ensures each function sorts public keys only once. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
|
@coderabbitai are you concerned about duplicated code between the new |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain@PastaPastaPasta, that's an excellent design question! Let me examine the actual implementations to assess the level of code duplication. Based on my examination of the code, I have some observations about the duplication concern: Good news: Limited duplication with smart design patterns
Areas where some duplication exists:
Assessment: This is actually a well-designed approach that minimizes duplication while maintaining API compatibility. The pattern of having existing methods delegate to The alternative approaches (like making the format parameter optional or using builder patterns) would be more invasive and potentially break existing code, which goes against the PR's goal of zero breaking changes. Recommendation: The current design strikes a good balance between functionality and maintainability. The duplication is minimal and justified by the backward compatibility requirements. 🏁 Script executed: Length of output: 808 🏁 Script executed: Length of output: 1046 🏁 Script executed: Length of output: 2105 🏁 Script executed: Length of output: 1244 Based on the actual code examination, I need to revise my assessment. Yes, there is indeed significant code duplication that should be a concern. Key findings:
Recommended refactoring to reduce duplication:
Your concern is absolutely valid - this level of duplication increases maintenance burden and the risk of bugs when logic diverges between the parallel implementations. The current approach prioritizes API compatibility over code maintainability, which might be acceptable for a compatibility layer but should be addressed in future iterations. |
Implement Option 2: Share implementation without forcing trait bounds - Add aggregate_secure_internal generic function that accepts custom serialization and hashing functions - Add verify_secure_with_dst_internal generic function for verification - Refactor all public functions to delegate to internal implementations - Eliminate ~150 lines of duplicate code - No breaking changes - public APIs remain unchanged - No trait bounds forced on users who don't need legacy support - Use inline hints to ensure zero performance overhead This approach provides code reuse while maintaining a clean API that doesn't require legacy trait implementations for standard usage. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/secure_aggregation.rs (1)
262-331: Consider extracting coefficient generation into a shared helper function.The coefficient generation logic (lines 286-326) is nearly identical to the standard version (lines 54-106), differing only in the input data source. While the current approach prioritizes clarity, extracting this logic into a shared helper function could reduce duplication and improve maintainability.
Consider creating a helper function like:
fn generate_coefficients_from_hash<C: BlsSignatureImpl>( base_hash: [u8; 32], count: usize, ) -> BlsResult<Vec<<<C as Pairing>::PublicKey as Group>::Scalar>> { // Extract the coefficient generation logic here }This would allow both
hash_public_keys_with_sortedandhash_public_keys_with_sorted_modeto share the same coefficient generation implementation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/secure_aggregation.rs(7 hunks)
🔇 Additional comments (4)
src/secure_aggregation.rs (4)
28-33: LGTM: Type alias improves code readability.The type alias
SortedKeysWithCoefficientsmakes the function signatures more readable and self-documenting.
112-167: LGTM: Well-designed internal function enables code reuse.The refactoring to
aggregate_secure_internalwith customizable serialization and hashing functions is an excellent design choice. It eliminates code duplication while maintaining clean, focused public APIs.
169-217: LGTM: Consistent internal function pattern.The
verify_secure_with_dst_internalfunction follows the same well-designed pattern as the aggregation function, providing customizable hashing while maintaining the existing API.
334-425: LGTM: Legacy-aware functions are well-implemented.The legacy-aware secure aggregation functions properly:
- Use the internal implementations to avoid code duplication
- Apply appropriate type constraints (
C::PublicKey: LegacyG1Point)- Pass the serialization format parameter correctly
- Maintain consistent error handling and validation
The API design maintains backward compatibility while enabling legacy format support.
- Extract common bit manipulation logic into helper functions: - modern_to_legacy_format() for serialization - legacy_to_modern_format() for deserialization - validate_modern_format() for format validation - Define clear constants for all bit manipulation values - Reduce ~200 lines of duplicated code between G1 and G2 implementations - Maintain exact same functionality with improved maintainability All tests pass with no changes to the public API. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add detailed documentation with examples for AggregateSignature, Signature, and SerializationFormat - Fix error enum naming (SerializationError was incorrectly named DeserializationError) - Improve ConditionallySelectable implementations with debug assertions and better fallback behavior - Replace unwrap() with expect() for better error messages in serialization - Document the difference between Modern (IETF) and Legacy (Dash-compatible) formats 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
055bf3c to
e2bc0c9
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/secure_aggregation.rs (1)
251-425: Address potential code duplication concern from PR objectives.While the implementation is correct and well-designed, there's still significant code duplication between the legacy-aware functions and their standard counterparts. The PR objectives mention this as a concern for maintainability.
Consider future refactoring to:
- Extract more common logic into shared helper functions
- Use default parameters or trait-based approaches to reduce API surface area
- Ensure validation logic remains consistent between parallel implementations
The current design prioritizes backward compatibility and clarity, which is appropriate for this initial implementation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
README.md(1 hunks)src/aggregate_signature.rs(5 hunks)src/error.rs(1 hunks)src/impls.rs(1 hunks)src/impls/legacy.rs(1 hunks)src/lib.rs(3 hunks)src/public_key.rs(1 hunks)src/public_key_share.rs(2 hunks)src/secure_aggregation.rs(7 hunks)src/serialization.rs(1 hunks)src/signature.rs(5 hunks)src/signature_share.rs(2 hunks)src/traits.rs(2 hunks)src/traits/legacy_serdes.rs(1 hunks)tests/cpp_integration_test.rs(5 hunks)tests/legacy_comprehensive_test.rs(1 hunks)tests/legacy_test.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
- src/impls.rs
- src/signature_share.rs
- tests/cpp_integration_test.rs
- src/public_key_share.rs
- src/traits.rs
- src/aggregate_signature.rs
- src/serialization.rs
- src/lib.rs
- src/public_key.rs
- src/traits/legacy_serdes.rs
- src/signature.rs
- src/impls/legacy.rs
🧰 Additional context used
🪛 LanguageTool
README.md
[grammar] ~83-~83: Use proper spacing conventions.
Context: ...8); ``` ## Legacy Serialization Support This library supports both modern (IETF ...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~85-~85: Use proper spacing conventions.
Context: ...h-compatible) BLS serialization formats. ### When to Use Legacy Mode Use legacy mode...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~87-~87: Use proper spacing conventions.
Context: ...on formats. ### When to Use Legacy Mode Use legacy mode when: - Interoperating w...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~90-~90: Use articles correctly.
Context: ...legacy mode when: - Interoperating with Dash blockchain - Working with data serializ...
(QB_NEW_EN_OTHER_ERROR_IDS_000004)
[grammar] ~92-~92: Use proper spacing conventions.
Context: ...ward compatibility with existing systems ### Serialization Examples ```rust // Moder...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~94-~94: Use proper spacing conventions.
Context: ...ting systems ### Serialization Examples rust // Modern format (default) let pk_bytes = public_key.to_bytes(); let pk = PublicKey::try_from(&pk_bytes[..]).unwrap(); // Legacy format let pk_legacy_bytes = public_key.to_bytes_with_mode(SerializationFormat::Legacy); let pk = PublicKey::from_bytes_with_mode(&pk_legacy_bytes, SerializationFormat::Legacy).unwrap(); ### VerifySecure with Legacy Support When u...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~106-~106: Use proper spacing conventions.
Context: ...`` ### VerifySecure with Legacy Support When using secure aggregation (VerifySec...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~108-~108: Use proper spacing conventions.
Context: ...ion mode affects coefficient generation: rust // Modern secure aggregation let agg_sig = aggregate_secure(&public_keys, &signatures).unwrap(); assert!(agg_sig.verify_secure(&public_keys, msg).is_ok()); // Legacy secure aggregation let agg_sig = aggregate_secure(&public_keys, &signatures).unwrap(); assert!(agg_sig.verify_secure_with_mode(&public_keys, msg, SerializationFormat::Legacy).is_ok()); Important: Signatures aggregated with ...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
[grammar] ~120-~120: Use proper spacing conventions.
Context: ... modes will cause verification failures. ## License Licensed under either of * Ap...
(QB_NEW_EN_OTHER_ERROR_IDS_000007)
🔇 Additional comments (9)
README.md (1)
83-121: Excellent documentation for legacy serialization support!The new documentation section is comprehensive and well-structured, providing clear guidance on:
- When to use legacy mode (Dash blockchain compatibility, older BLS libraries)
- Serialization examples for both modern and legacy formats
- Secure aggregation with legacy support
- Important security note about mixing modes
The examples correctly demonstrate the API usage and the security warning about mode consistency is crucial for proper implementation.
src/error.rs (1)
27-54: Well-designed error enhancements for legacy serialization support.The new error variants provide excellent specificity for different failure modes:
LegacyFormatError- specific to legacy format issuesCrossFormatError- structured with format and mode fields for clear diagnosticsInvalidLength- structured with expected/actual values for debuggingThe error messages are clear and informative, and the structured approach will greatly help with debugging serialization issues.
tests/legacy_test.rs (1)
1-247: Comprehensive and well-structured test suite.This test suite provides excellent coverage of legacy serialization functionality:
- Roundtrip testing for both G1 and G2 points in legacy/modern formats
- Cross-format validation ensuring incompatible formats properly fail
- Secure aggregation testing with mode-specific verification
- Edge case handling including infinity points and specific bit patterns
- Error condition testing for malformed data and invalid lengths
The test organization is clear with descriptive names, and the assertions properly validate expected behaviors. The search for Y=0 cases (lines 87-105) is particularly thorough for testing format incompatibility.
tests/legacy_comprehensive_test.rs (1)
1-436: Excellent comprehensive test coverage with robust validation.This test suite provides exceptional coverage across multiple dimensions:
- All signature schemes (Basic, MessageAugmentation, ProofOfPossession)
- Complex aggregation scenarios including multi-signature and threshold signatures
- Randomized testing with 100 seeds for robustness (lines 243-288)
- Edge case validation including zero scalar, mixed Y coordinates, and malformed data
- Performance scenarios with batch verification
The randomized testing approach is particularly valuable for catching edge cases that might not be covered by deterministic tests. The comprehensive coverage of all signature schemes and aggregation types ensures robust validation of the legacy serialization support.
src/secure_aggregation.rs (5)
28-33: Good type alias for improved code readability.The
SortedKeysWithCoefficientstype alias clearly documents the return type and makes the code more readable and maintainable.
111-167: Excellent refactoring with internal implementation pattern.The refactoring to use
aggregate_secure_internalwith customizable serialization and hashing functions is well-designed:
- Separation of concerns - logic separated from serialization details
- Reusability - internal implementation can be used by both standard and legacy functions
- Maintainability - reduces code duplication while maintaining clean APIs
- Flexibility - allows different serialization formats without duplicating core logic
This pattern effectively addresses the code duplication concerns mentioned in the PR objectives.
169-217: Consistent internal implementation pattern for verification.The same excellent refactoring pattern is applied to verification functions with
verify_secure_with_dst_internal. This provides the same benefits of reduced duplication and improved maintainability.
251-331: Well-implemented legacy-aware coefficient generation.The
hash_public_keys_with_sorted_modefunction properly handles legacy serialization:
- Correct serialization using
to_bytes_with_modefor format-specific serialization- Consistent hashing maintains the same coefficient generation algorithm as the standard version
- Proper error handling for edge cases and invalid inputs
- Clear documentation about endianness handling alignment
The implementation correctly generates different coefficients for different serialization modes, which is crucial for security when mixing legacy and modern formats.
333-425: Comprehensive legacy-aware secure aggregation API.The complete set of legacy-aware functions provides excellent coverage:
- Aggregation support with
aggregate_secure_with_mode- All signature schemes covered with corresponding verification functions
- Consistent API design following the same patterns as standard functions
- Proper trait bounds requiring
LegacyG1Pointfor legacy supportThe API design maintains backward compatibility while providing the necessary legacy support for interoperability with older systems.
Summary
This PR adds comprehensive support for the legacy BLS serialization format used by Dash and other systems, enabling interoperability with older implementations while maintaining full backward compatibility.
Motivation
The C++ bls-signatures library supports a
fLegacyparameter that controls serialization format for backward compatibility. This is essential for:Changes
Core Implementation
src/traits/legacy_serdes.rs): Define traits for legacy format supportsrc/impls/legacy.rs): Implement bit-level conversion between legacy and modern formats_with_modemethods to PublicKey and Signature typesFormat Differences
Key Features
Test Coverage
Added extensive test suites:
tests/legacy_test.rs: Core functionality teststests/legacy_comprehensive_test.rs: Edge cases, all signature schemes, threshold signatures, malformed dataAll tests passing with 100% of legacy functionality covered.
Usage Examples
Important Notes
legacy=truemust be verified withlegacy=true🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Bug Fixes
Tests
Style