malleable.mx raw

   1  // Anti-malleability defenses for homomorphic ciphertexts.
   2  package ring
   3  
   4  import (
   5  	"crypto/rand"
   6  	"io"
   7  
   8  	"crypto/sha3"
   9  )
  10  
  11  func NoiseFlood(pk *KEMPublicKey, ct *HECiphertext, floodBits int32) (result *HECiphertext) {
  12  	return NoiseFloodFrom(pk, ct, floodBits, rand.Reader)
  13  }
  14  
  15  func NoiseFloodFrom(pk *KEMPublicKey, ct *HECiphertext, floodBits int32, rng io.Reader) (result *HECiphertext) {
  16  	p := pk.P.Ring
  17  	q := p.Q
  18  
  19  	floodU := New(p)
  20  	floodV := New(p)
  21  
  22  	bound := uint32(1) << uint32(floodBits)
  23  
  24  	var buf [4]byte
  25  	for i := int32(0); i < p.N; i++ {
  26  		io.ReadFull(rng, buf[:])
  27  		val := uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])
  28  		val = val % (2 * bound)
  29  		noise := int64(val) - int64(bound)
  30  		noise = noise & ^int64(1)
  31  		if noise >= 0 {
  32  			floodU.Coeffs[i] = uint32(noise) % q
  33  		} else {
  34  			floodU.Coeffs[i] = q - (uint32(-noise) % q)
  35  		}
  36  
  37  		io.ReadFull(rng, buf[:])
  38  		val = uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])
  39  		val = val % (2 * bound)
  40  		noise = int64(val) - int64(bound)
  41  		noise = noise & ^int64(1)
  42  		if noise >= 0 {
  43  			floodV.Coeffs[i] = uint32(noise) % q
  44  		} else {
  45  			floodV.Coeffs[i] = q - (uint32(-noise) % q)
  46  		}
  47  	}
  48  
  49  	return &HECiphertext{
  50  		U:             Add(ct.U, floodU),
  51  		V:             Add(ct.V, floodV),
  52  		NoiseEstimate: ct.NoiseEstimate + float64(bound),
  53  		params:        ct.params,
  54  	}
  55  }
  56  
  57  type SecureHEResult struct {
  58  	Ciphertext *HECiphertext
  59  	Tag        []byte
  60  	Signature  *GPVSignature
  61  }
  62  
  63  func WrapResult(pk *KEMPublicKey, ct *HECiphertext, sessionID []byte, gpvSK *GPVSecretKey) (result *SecureHEResult) {
  64  	return WrapResultFrom(pk, ct, sessionID, gpvSK, rand.Reader)
  65  }
  66  
  67  func WrapResultFrom(pk *KEMPublicKey, ct *HECiphertext, sessionID []byte, gpvSK *GPVSecretKey, rng io.Reader) (result *SecureHEResult) {
  68  	ct = RerandomizeFrom(pk, ct, rng)
  69  
  70  	floodBits := int32(8)
  71  	ct = NoiseFloodFrom(pk, ct, floodBits, rng)
  72  
  73  	tag := computeTag(ct, sessionID)
  74  
  75  	var sig *GPVSignature
  76  	if gpvSK != nil {
  77  		sig = GPVSignFrom(gpvSK, tag, rng)
  78  	}
  79  
  80  	return &SecureHEResult{
  81  		Ciphertext: ct,
  82  		Tag:        tag,
  83  		Signature:  sig,
  84  	}
  85  }
  86  
  87  func VerifyResult(result *SecureHEResult, sessionID []byte, gpvPK *GPVPublicKey) (ok bool) {
  88  	expectedTag := computeTag(result.Ciphertext, sessionID)
  89  	if int32(len(result.Tag)) != int32(len(expectedTag)) {
  90  		return false
  91  	}
  92  	for i := int32(0); i < int32(len(result.Tag)); i++ {
  93  		if result.Tag[i] != expectedTag[i] {
  94  			return false
  95  		}
  96  	}
  97  
  98  	if result.Signature != nil {
  99  		if gpvPK == nil {
 100  			return false
 101  		}
 102  		if !GPVVerify(gpvPK, result.Tag, result.Signature) {
 103  			return false
 104  		}
 105  	}
 106  
 107  	return true
 108  }
 109  
 110  func computeTag(ct *HECiphertext, sessionID []byte) (tag []byte) {
 111  	h := sha3.NewSHAKE256()
 112  	h.Write([]byte("he-result-tag-v1"))
 113  	h.Write(sessionID)
 114  
 115  	uBytes := Serialize(ct.U)
 116  	vBytes := Serialize(ct.V)
 117  	h.Write(uBytes)
 118  	h.Write(vBytes)
 119  
 120  	tag = []byte{:32}
 121  	h.Read(tag)
 122  	return tag
 123  }
 124  
 125  type SessionWrapper struct {
 126  	SessionID []byte
 127  	HEPK      *KEMPublicKey
 128  	GPVPK     *GPVPublicKey
 129  	GPVSK     *GPVSecretKey
 130  }
 131  
 132  func NewSession(hePK *KEMPublicKey, gpvPK *GPVPublicKey, gpvSK *GPVSecretKey) (sw *SessionWrapper) {
 133  	sessionID := []byte{:32}
 134  	rand.Read(sessionID)
 135  
 136  	return &SessionWrapper{
 137  		SessionID: sessionID,
 138  		HEPK:      hePK,
 139  		GPVPK:     gpvPK,
 140  		GPVSK:     gpvSK,
 141  	}
 142  }
 143  
 144  func (sw *SessionWrapper) Wrap(ct *HECiphertext) (result *SecureHEResult) {
 145  	return WrapResult(sw.HEPK, ct, sw.SessionID, sw.GPVSK)
 146  }
 147  
 148  func (sw *SessionWrapper) Verify(result *SecureHEResult) (ok bool) {
 149  	return VerifyResult(result, sw.SessionID, sw.GPVPK)
 150  }
 151