(no commit message provided)

This commit is contained in:
Prad Nukala
2024-07-05 22:20:13 -04:00
committed by Prad Nukala (aider)
commit 5fd43dfd6b
457 changed files with 115535 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
# Zero-Knowledge, Post-Quantum Hybrid Encryption Scheme
`Secret`` is a cutting-edge encryption scheme developed by Sonr, combining zero-knowledge proofs, post-quantum cryptography, and hybrid encryption techniques. This innovative approach provides robust security for the decentralized identity ecosystem, ensuring data privacy and integrity in the face of current and future threats.
Features
Zero-Knowledge Proofs: Utilizes cryptographic accumulators for efficient membership verification without revealing sensitive information.
Post-Quantum Security: Employs Kyber768, a lattice-based key encapsulation mechanism (KEM) resistant to quantum computing attacks.
Hybrid Encryption: Combines the strengths of asymmetric and symmetric cryptography for optimal performance and security.
IPFS Integration: Seamlessly works with IPFS (InterPlanetary File System) for decentralized data storage.
Deterministic Key Derivation: Ensures consistent key generation based on accumulator state and IPFS vault CID.
## How It Works
### Encryption
1. Marshals the cryptographic accumulator.
2. Derives a Kyber keypair using the accumulator and IPFS vault CID.
3. Encapsulates a shared secret using Kyber768.
4. Encrypts the message using AES-GCM with the shared secret.
5. Prepends the marshaled accumulator to the encrypted data.
### Decryption
1. Extracts and unmarshals the accumulator from the encrypted data.
2. Derives the Kyber keypair using the extracted accumulator.
3. Decapsulates the shared secret.
4. Decrypts the message using AES-GCM.
5. Verifies the witness against the extracted accumulator.
## Security Considerations
- Post-quantum secure due to the use of Kyber768.
- Zero-knowledge proofs protect sensitive information during verification.
- Hybrid approach combines the security of asymmetric cryptography with the efficiency of symmetric encryption.
- Accumulator-based access control adds an extra layer of security.
## Use Cases
- Secure data sharing in decentralized identity systems.
- Privacy-preserving credential verification.
- Quantum-resistant communication for long-term data protection.
- Decentralized access control for sensitive information.
+149
View File
@@ -0,0 +1,149 @@
package secret
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"github.com/cloudflare/circl/kem/kyber/kyber768"
"github.com/onsonr/hway/crypto/accumulator"
"github.com/ipfs/go-cid"
)
const AccumulatorMarshalledSize = 60
func (s *PrimaryKey) Encrypt(acc *accumulator.Accumulator, vaultCID string, message []byte) ([]byte, error) {
pub, _, err := deriveKyberKeypair(acc, vaultCID)
if err != nil {
return nil, err
}
ct := make([]byte, kyber768.CiphertextSize)
ss := make([]byte, kyber768.SharedKeySize)
pub.EncapsulateTo(ct, ss, nil)
block, err := aes.NewCipher(ss)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create GCM: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, fmt.Errorf("failed to generate nonce: %w", err)
}
accBytes, err := acc.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("failed to marshal accumulator: %w", err)
}
if len(accBytes) != AccumulatorMarshalledSize {
return nil, fmt.Errorf("unexpected accumulator marshalled size: got %d, want %d", len(accBytes), AccumulatorMarshalledSize)
}
paddedMessage := append(accBytes, message...)
encryptedMessage := gcm.Seal(nil, nonce, paddedMessage, nil)
result := append(ct, nonce...)
result = append(result, encryptedMessage...)
return result, nil
}
// Decrypt is
func (s *PrimaryKey) Decrypt(vaultCID string, encryptedData []byte, witness *accumulator.MembershipWitness, pubKey *accumulator.PublicKey) ([]byte, error) {
if len(encryptedData) < kyber768.CiphertextSize+AccumulatorMarshalledSize {
return nil, fmt.Errorf("invalid encrypted data: too short")
}
// Extract and unmarshal the accumulator from the first 60 bytes
var decryptedAcc accumulator.Accumulator
err := decryptedAcc.UnmarshalBinary(encryptedData[:AccumulatorMarshalledSize])
if err != nil {
return nil, fmt.Errorf("failed to unmarshal accumulator: %w", err)
}
// Derive Kyber keypair using the unmarshalled accumulator
_, priv, err := deriveKyberKeypair(&decryptedAcc, vaultCID)
if err != nil {
return nil, err
}
// Decapsulate the shared secret
ct := encryptedData[AccumulatorMarshalledSize : AccumulatorMarshalledSize+kyber768.CiphertextSize]
ss := make([]byte, kyber768.SharedKeySize)
priv.DecapsulateTo(ss, ct)
// Set up AES-GCM decryption
block, err := aes.NewCipher(ss)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create GCM: %w", err)
}
nonceSize := gcm.NonceSize()
if len(encryptedData) < AccumulatorMarshalledSize+kyber768.CiphertextSize+nonceSize {
return nil, fmt.Errorf("invalid encrypted data: too short for nonce")
}
nonce := encryptedData[AccumulatorMarshalledSize+kyber768.CiphertextSize : AccumulatorMarshalledSize+kyber768.CiphertextSize+nonceSize]
ciphertext := encryptedData[AccumulatorMarshalledSize+kyber768.CiphertextSize+nonceSize:]
// Decrypt the message
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("failed to decrypt: %w", err)
}
// Verify the witness using the decrypted accumulator and provided secret key
if err := witness.Verify(pubKey, &decryptedAcc); err != nil {
return nil, fmt.Errorf("unauthorized witness: %w", err)
}
return plaintext, nil
}
func deriveKyberKeypair(acc *accumulator.Accumulator, vaultCID string) (*kyber768.PublicKey, *kyber768.PrivateKey, error) {
seed, err := generateDeterministicSeed(acc, vaultCID)
if err != nil {
return nil, nil, err
}
// Ensure the seed is the correct size for Kyber768
if len(seed) < kyber768.KeySeedSize {
expandedSeed := make([]byte, kyber768.KeySeedSize)
copy(expandedSeed, seed)
seed = expandedSeed
}
pub, priv := kyber768.NewKeyFromSeed(seed[:kyber768.KeySeedSize])
return pub, priv, nil
}
func generateDeterministicSeed(acc *accumulator.Accumulator, vaultCID string) ([]byte, error) {
_, err := cid.Decode(vaultCID)
if err != nil {
return nil, fmt.Errorf("invalid IPFS CID: %w", err)
}
accBytes, err := acc.MarshalBinary()
if err != nil {
return nil, err
}
data := append(accBytes, []byte(vaultCID)...)
hash := sha256.Sum256(data)
return hash[:], nil
}
+107
View File
@@ -0,0 +1,107 @@
package secret
import (
"errors"
"fmt"
"github.com/onsonr/hway/crypto"
"github.com/onsonr/hway/crypto/accumulator"
"github.com/onsonr/hway/crypto/core/curves"
)
// PrimaryKey is the secret key for the BLS scheme
type PrimaryKey struct {
*accumulator.SecretKey
}
// Element is the element for the BLS scheme
type Element = accumulator.Element
// NewKey creates a new primary key
func NewKey(propertyKey string, pubKey crypto.PublicKey) (*PrimaryKey, error) {
// Concatenate the controller's public key and the property key
input := append(pubKey.Bytes(), []byte(propertyKey)...)
hash := []byte(input)
// Use the hash as the seed for the secret key
curve := curves.BLS12381(&curves.PointBls12381G1{})
key, err := new(accumulator.SecretKey).New(curve, hash[:])
if err != nil {
return nil, errors.Join(err, fmt.Errorf("failed to create secret key"))
}
return &PrimaryKey{SecretKey: key}, nil
}
// CreateAccumulator creates a new accumulator
func (s *PrimaryKey) CreateAccumulator(values ...string) (*accumulator.Accumulator, error) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
acc, err := new(accumulator.Accumulator).New(curve)
if err != nil {
return nil, err
}
fin, _, err := acc.Update(s.SecretKey, convertValuesToElements(values), nil)
if err != nil {
return nil, err
}
return fin, nil
}
// CreateWitness creates a witness for the accumulator for a given value
func (s *PrimaryKey) CreateWitness(acc *accumulator.Accumulator, value string) (*accumulator.MembershipWitness, error) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
element := curve.Scalar.Hash([]byte(value))
mw, err := new(accumulator.MembershipWitness).New(element, acc, s.SecretKey)
if err != nil {
return nil, err
}
return mw, nil
}
// ProveMembership proves that a value is a member of the accumulator
func (s *PrimaryKey) VerifyWitness(acc *accumulator.Accumulator, witness *accumulator.MembershipWitness) error {
return witness.Verify(s.PublicKey(), acc)
}
// PublicKey returns the public key for the secret key
func (s *PrimaryKey) PublicKey() *accumulator.PublicKey {
curve := curves.BLS12381(&curves.PointBls12381G1{})
pk, err := s.GetPublicKey(curve)
if err != nil {
panic(err)
}
return pk
}
// UpdateAccumulator updates the accumulator with new values
func (s *PrimaryKey) UpdateAccumulator(acc *accumulator.Accumulator, addValues, removeValues []string) (*accumulator.Accumulator, error) {
acc, _, err := acc.Update(s.SecretKey, convertValuesToElements(addValues), convertValuesToElements(removeValues))
if err != nil {
return nil, err
}
return acc, nil
}
// MarshalAccumulator takes a *accumulator.Accumulator and returns a byte slice
func MarshalAccumulator(acc *accumulator.Accumulator) ([]byte, error) {
return acc.MarshalBinary()
}
// UnmarshalAccumulator takes a byte slice and returns a *accumulator.Accumulator
func UnmarshalAccumulator(data []byte) (*accumulator.Accumulator, error) {
acc := new(accumulator.Accumulator)
err := acc.UnmarshalBinary(data)
if err != nil {
return nil, err
}
return acc, nil
}
func convertValuesToElements(values []string) []accumulator.Element {
curve := curves.BLS12381(&curves.PointBls12381G1{})
elements := []accumulator.Element{}
for _, value := range values {
element := curve.Scalar.Hash([]byte(value))
elements = append(elements, element)
}
return elements
}