mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 18:01:39 +00:00
(no commit message provided)
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user