diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..04d7376 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,31 @@ +name: .NET Build and Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up .NET SDK + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '8.0' + + - name: Restore dependencies + run: dotnet restore + + - name: Build the project + run: dotnet build --configuration Release + + - name: Run tests + run: dotnet test --configuration Release --no-build --verbosity normal \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cbca89e..07b9753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [Unreleased] +## [1.1.1] - 2025-01-01 ### Added - New method for range proofs (`ProveRange` and `VerifyRange`). @@ -8,7 +8,7 @@ - Support for proving set membership (`ProveSetMembership` and `VerifySetMembership`). ### Changed -- Updated HMAC implementation to use more secure random salts. +- Refined HMAC implementation to retrieve the secret key from environment variables for enhanced security and flexibility. ### Fixed - Bug in age verification logic that caused incorrect validation for dates close to the required age. diff --git a/README.md b/README.md index 2e2b141..9ba9ec7 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,12 @@ - **Proof of Age**: Prove that your age is above a certain threshold without revealing your actual birthdate. - **Proof of Balance**: Prove that you have sufficient balance to make a transaction without revealing your full balance. +- **Proof of Membership**: Prove that a given value belongs to a set of valid values (e.g., proving you belong to a specific group). +- **Proof of Range**: Prove that a value lies within a specified range without revealing the exact value. +- **Proof of Time Condition**: Prove that an event occurred before or after a specified date without revealing the event date. - **Secure Hashing**: Uses SHA-256 hashing combined with a random salt to ensure secure and non-reversible proofs. + ## Installation You can install **ZkpSharp** via NuGet. Run the following command in your project directory: @@ -15,6 +19,45 @@ You can install **ZkpSharp** via NuGet. Run the following command in your projec ```bash dotnet add package ZkpSharp ``` + +## Setup + +Before using the ZkpSharp library, you need to configure a secret key for HMAC (SHA-256) hashing. This key is required for generating and verifying proofs. + +### Setting Up the HMAC Key in Code + +Instead of using environment variables, you can pass the HMAC secret key directly when creating the ProofProvider. The key should be a 256-bit key (32 bytes) encoded in Base64. + +Here’s an example of how to configure the HMAC key directly in your application: + +```csharp +using ZkpSharp; +using ZkpSharp.Security; +using System; + +class Program +{ + static void Main() + { + // Example base64-encoded HMAC secret key (256 bits / 32 bytes) + string hmacSecretKeyBase64 = "your-base64-encoded-key-here"; + + // Create an instance of ProofProvider with the provided HMAC key + var proofProvider = new ProofProvider(hmacSecretKeyBase64); + + var zkp = new ZKP(proofProvider); + var dateOfBirth = new DateTime(2000, 1, 1); // The user's date of birth + + // Generate proof of age + var (proof, salt) = zkp.ProveAge(dateOfBirth); + + // Verify the proof of age + bool isValid = zkp.VerifyAge(proof, dateOfBirth, salt); + Console.WriteLine($"Age proof valid: {isValid}"); + } +} +``` + ## Usage ### Proof of Age @@ -70,14 +113,16 @@ class Program } } ``` + ## Contributing We welcome contributions! To contribute: - 1. Fork the repository. - 2. Create a new branch for your changes (git checkout -b feature/your-feature). - 3. Commit your changes (git commit -m 'Add new feature'). - 4. Push to your branch (git push origin feature/your-feature). - 5. Create a pull request. + +1. Fork the repository. +2. Create a new branch for your changes (`git checkout -b feature/your-feature`). +3. Commit your changes (`git commit -m 'Add new feature'`). +4. Push to your branch (`git push origin feature/your-feature`). +5. Create a pull request. Please ensure that your code passes all tests and adheres to the code style of the project. @@ -85,11 +130,6 @@ Please ensure that your code passes all tests and adheres to the code style of t This project is licensed under the MIT License - see the LICENSE file for details. -## Roadmap - • Add more proof types (e.g., Proof of Identity, Proof of Transaction History). - • Improve performance and scalability for large datasets. - • Enhance security features (e.g., multi-factor authentication for proofs). - ## Contact For questions, issues, or suggestions, feel free to open an issue or contact Azimbek Sagynbaev at [sagynbaev6@gmail.com]. \ No newline at end of file diff --git a/ZkpSharp.Tests/ZKPTests.cs b/ZkpSharp.Tests/ZKPTests.cs index 728b685..8e57788 100644 --- a/ZkpSharp.Tests/ZKPTests.cs +++ b/ZkpSharp.Tests/ZKPTests.cs @@ -10,7 +10,7 @@ public class ZKPTests [Fact] public void TestProveAndVerifyAge_ValidAge_ShouldPass() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); var dateOfBirth = new DateTime(2000, 1, 1); // Age 25 var (proof, salt) = zkp.ProveAge(dateOfBirth); @@ -21,7 +21,7 @@ public void TestProveAndVerifyAge_ValidAge_ShouldPass() [Fact] public void TestProveAndVerifyAge_InsufficientAge_ShouldFail() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); var dateOfBirth = new DateTime(2010, 1, 1); // Age 15 @@ -32,7 +32,7 @@ public void TestProveAndVerifyAge_InsufficientAge_ShouldFail() [Fact] public void TestProveAndVerifyBalance_ValidBalance_ShouldPass() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); double userBalance = 1000.0; double requestedAmount = 500.0; @@ -46,7 +46,7 @@ public void TestProveAndVerifyBalance_ValidBalance_ShouldPass() [Fact] public void TestProveAndVerifyBalance_InsufficientBalance_ShouldFail() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); double userBalance = 300.0; double requestedAmount = 500.0; @@ -59,7 +59,7 @@ public void TestProveAndVerifyBalance_InsufficientBalance_ShouldFail() [Fact] public void TestBalanceVerificationWithSalt_ValidBalance_ShouldPass() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); double userBalance = 1000.0; double requestedAmount = 500.0; @@ -71,7 +71,7 @@ public void TestBalanceVerificationWithSalt_ValidBalance_ShouldPass() [Fact] public void TestBalanceVerificationWithSalt_InsufficientBalance_ShouldFail() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); double userBalance = 100.0; double requestedAmount = 150.0; @@ -83,7 +83,7 @@ public void TestBalanceVerificationWithSalt_InsufficientBalance_ShouldFail() [Fact] public void TestProveAndVerifyAge_InvalidSalt_ShouldFail() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); var dateOfBirth = new DateTime(2000, 1, 1); // Возраст 25 лет var (proof, salt) = zkp.ProveAge(dateOfBirth); @@ -96,7 +96,7 @@ public void TestProveAndVerifyAge_InvalidSalt_ShouldFail() [Fact] public void TestBalanceVerificationWithSalt_InvalidSalt_ShouldFail() { - var proofProvider = new ProofProvider(); + var proofProvider = new ProofProvider("hmacSecretKeyBase64"); var zkp = new ZKP(proofProvider); double userBalance = 1000.0; double requestedAmount = 500.0; @@ -105,5 +105,8 @@ public void TestBalanceVerificationWithSalt_InvalidSalt_ShouldFail() string incorrectSalt = Guid.NewGuid().ToString(); Assert.False(zkp.VerifyBalance(proof, requestedAmount, incorrectSalt, userBalance), "Proof should fail due to incorrect salt"); } + + // TODO: Add more tests for: + // ProveRange, VerifyRange, ProveTimestamp, VerifyTimestamp, ProveSetMembership, VerifySetMembership. } } \ No newline at end of file diff --git a/ZkpSharp/Core/ZKP.cs b/ZkpSharp/Core/ZKP.cs index 5cdc20f..5d94e2d 100644 --- a/ZkpSharp/Core/ZKP.cs +++ b/ZkpSharp/Core/ZKP.cs @@ -58,5 +58,79 @@ public bool VerifyBalance(string proof, double requestedAmount, string salt, dou string calculatedProof = _proofProvider.GenerateHMAC(balance.ToString() + salt); return _proofProvider.SecureEqual(calculatedProof, proof) && balance >= requestedAmount; } + + // Proof of Membership + public string ProveMembership(string value, string[] validValues) + { + string salt = _proofProvider.GenerateSalt(); + var validValueHash = validValues.Select(v => _proofProvider.GenerateHMAC(v)).ToArray(); + + foreach (var hash in validValueHash) + { + if (_proofProvider.SecureEqual(_proofProvider.GenerateHMAC(value + salt), hash)) + { + return salt; + } + } + + throw new ArgumentException("Value does not belong to the set."); + } + + public bool VerifyMembership(string value, string salt, string[] validValues) + { + var validValueHash = validValues.Select(v => _proofProvider.GenerateHMAC(v)).ToArray(); + string proof = _proofProvider.GenerateHMAC(value + salt); + + return validValueHash.Contains(proof); + } + + // Proof of Range + public string ProveRange(double value, double minValue, double maxValue) + { + if (value < minValue || value > maxValue) + { + throw new ArgumentException("Value out of range."); + } + + string salt = _proofProvider.GenerateSalt(); + string proof = _proofProvider.GenerateHMAC(value.ToString() + salt); + return proof; + } + + public bool VerifyRange(string proof, double minValue, double maxValue, double value) + { + if (value < minValue || value > maxValue) + { + return false; + } + + string calculatedProof = _proofProvider.GenerateHMAC(value.ToString() + _proofProvider.GenerateSalt()); + return _proofProvider.SecureEqual(proof, calculatedProof); + } + + + // Proof of Time Condition + public string ProveTimeCondition(DateTime eventDate, DateTime conditionDate) + { + if (eventDate < conditionDate) + { + throw new ArgumentException("Event does not meet the time condition."); + } + + string salt = _proofProvider.GenerateSalt(); + string proof = _proofProvider.GenerateHMAC(eventDate.ToString("yyyy-MM-dd") + salt); + return proof; + } + + public bool VerifyTimeCondition(string proof, DateTime eventDate, DateTime conditionDate, string salt) + { + if (eventDate < conditionDate) + { + return false; + } + + string calculatedProof = _proofProvider.GenerateHMAC(eventDate.ToString("yyyy-MM-dd") + salt); + return _proofProvider.SecureEqual(proof, calculatedProof); + } } } \ No newline at end of file diff --git a/ZkpSharp/Security/ProofProvider.cs b/ZkpSharp/Security/ProofProvider.cs index 0a1b993..c265b2a 100644 --- a/ZkpSharp/Security/ProofProvider.cs +++ b/ZkpSharp/Security/ProofProvider.cs @@ -14,9 +14,9 @@ public class ProofProvider : IProofProvider { private readonly byte[] _hmacKey; - public ProofProvider() + public ProofProvider(string hmacSecretKeyBase64) { - _hmacKey = LoadHmacKey(); + _hmacKey = Convert.FromBase64String(hmacSecretKeyBase64); } public string GenerateSalt() @@ -50,11 +50,5 @@ public bool SecureEqual(string a, string b) return diff == 0; } - - private byte[] LoadHmacKey() - { - // Load HMAC key from secure storage - return new byte[32]; // Here we just return a dummy key - } } } \ No newline at end of file diff --git a/ZkpSharp/ZkpSharp.csproj b/ZkpSharp/ZkpSharp.csproj index 013cc5b..f443697 100644 --- a/ZkpSharp/ZkpSharp.csproj +++ b/ZkpSharp/ZkpSharp.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 1.1.0 + 1.1.1 README.md