Skip to content

Commit

Permalink
feat: add true randomness
Browse files Browse the repository at this point in the history
  • Loading branch information
immortal-tofu committed Jun 14, 2024
1 parent 8165b6d commit f00a728
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 53 deletions.
66 changes: 13 additions & 53 deletions fhevm/operators_rand.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package fhevm

import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"math/big"
"math/bits"
"unsafe"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -60,7 +58,7 @@ func applyUpperBound(rand uint64, bitsInRand int, upperBound *uint64) uint64 {
return rand >> shift
}

func generateRandom(environment EVMEnvironment, caller common.Address, resultType tfhe.FheUintType, upperBound *uint64) ([]byte, error) {
func generateRandom(environment EVMEnvironment, caller common.Address, resultType tfhe.FheUintType, numberOfBits uint64) ([]byte, error) {
// If we are doing gas estimation, skip execution and insert a random ciphertext as a result.
if !environment.IsCommitting() {
return insertRandomCiphertext(environment, resultType), nil
Expand All @@ -84,58 +82,15 @@ func generateRandom(environment EVMEnvironment, caller common.Address, resultTyp
if err != nil {
return nil, err
}
// The RNG nonce bytes are of size chacha20.NonceSizeX, which is assumed to be 24 bytes (see init() above).
// Since uint256.Int.z[0] is the least significant byte and since uint256.Int.Bytes32() serializes
// in order of z[3], z[2], z[1], z[0], we want to essentially ignore the first byte, i.e. z[3], because
// it will always be 0 as the nonce size is 24.
cipher, err := chacha20.NewUnauthenticatedCipher(seed.Bytes(), currentRngNonceBytes[32-chacha20.NonceSizeX:32])
if err != nil {
return nil, err
}

// XOR a byte array of 0s with the stream from the cipher and receive the result in the same array.
// Apply upperBound, if set.
var randUint uint64
switch resultType {
case tfhe.FheUint4:
randBytes := make([]byte, 1)
cipher.XORKeyStream(randBytes, randBytes)
randUint = uint64(randBytes[0])
randUint = uint64(applyUpperBound(randUint, 4, upperBound))
case tfhe.FheUint8:
randBytes := make([]byte, 1)
cipher.XORKeyStream(randBytes, randBytes)
randUint = uint64(randBytes[0])
randUint = uint64(applyUpperBound(randUint, 8, upperBound))
case tfhe.FheUint16:
randBytes := make([]byte, 2)
cipher.XORKeyStream(randBytes, randBytes)
randUint = uint64(binary.BigEndian.Uint16(randBytes))
randUint = uint64(applyUpperBound(randUint, 16, upperBound))
case tfhe.FheUint32:
randBytes := make([]byte, 4)
cipher.XORKeyStream(randBytes, randBytes)
randUint = uint64(binary.BigEndian.Uint32(randBytes))
randUint = uint64(applyUpperBound(randUint, 32, upperBound))
case tfhe.FheUint64:
randBytes := make([]byte, 8)
cipher.XORKeyStream(randBytes, randBytes)
randUint = uint64(binary.BigEndian.Uint64(randBytes))
randUint = uint64(applyUpperBound(randUint, 64, upperBound))
default:
return nil, fmt.Errorf("generateRandom() invalid type requested: %d", resultType)
}

// Trivially encrypt the random integer.
randCt := new(tfhe.TfheCiphertext)
randBigInt := big.NewInt(0)
randBigInt.SetUint64(randUint)
randCt.TrivialEncrypt(*randBigInt, resultType)
insertCiphertextToMemory(environment, randCt)
randCt, err := tfhe.GenerateObliviousPseudoRandom(resultType, *(*uint64)(unsafe.Pointer(&seed.Bytes()[0])), numberOfBits)

if err != nil {
return nil, err
}

insertCiphertextToMemory(environment, randCt)

ctHash := randCt.GetHash()
return ctHash[:], nil
}
Expand All @@ -156,7 +111,7 @@ func fheRandRun(environment EVMEnvironment, caller common.Address, addr common.A
}
resultType := tfhe.FheUintType(input[0])
otelDescribeOperandsFheTypes(runSpan, resultType)
var noUpperBound *uint64 = nil
var noUpperBound uint64 = uint64(resultType.NumBits())
return generateRandom(environment, caller, resultType, noUpperBound)
}

Expand All @@ -177,5 +132,10 @@ func fheRandBoundedRun(environment EVMEnvironment, caller common.Address, addr c
return nil, errors.New(msg)
}
bound64 := bound.Uint64()
return generateRandom(environment, caller, randType, &bound64)
numberOfBits := uint64(1);
for bound64 > uint64(1) {
bound64 = bound64 / uint64(2);
numberOfBits++;
}
return generateRandom(environment, caller, randType, numberOfBits)
}
67 changes: 67 additions & 0 deletions fhevm/tfhe/tfhe_ciphertext.go
Original file line number Diff line number Diff line change
Expand Up @@ -2680,3 +2680,70 @@ func EqArray(lhs []*TfheCiphertext, rhs []*TfheCiphertext) (*TfheCiphertext, err
}
return result, nil
}

func GenerateObliviousPseudoRandom(generatedType FheUintType, seed uint64, numberOfBits uint64) (*TfheCiphertext, error) {
result := new(TfheCiphertext)

// Do the FHE computation.
var resultPtr unsafe.Pointer
switch generatedType {
case FheUint4:
resultPtr = C.generate_oblivious_pseudo_random_uint4(C.uint64_t(seed), C.uint64_t(numberOfBits), sks)
case FheUint8:
resultPtr = C.generate_oblivious_pseudo_random_uint8(C.uint64_t(seed), C.uint64_t(numberOfBits), sks)
case FheUint16:
resultPtr = C.generate_oblivious_pseudo_random_uint16(C.uint64_t(seed), C.uint64_t(numberOfBits), sks)
case FheUint32:
resultPtr = C.generate_oblivious_pseudo_random_uint32(C.uint64_t(seed), C.uint64_t(numberOfBits), sks)
case FheUint64:
resultPtr = C.generate_oblivious_pseudo_random_uint64(C.uint64_t(seed), C.uint64_t(numberOfBits), sks)
default:
return nil, fmt.Errorf("GenerateObliviousPseudoRandom: unsupported ciphertext type %d", generatedType)
}
if resultPtr == nil {
return nil, errors.New("GenerateObliviousPseudoRandom: generation failed")
}
defer C.destroy_fhe_bool(resultPtr)
ser := &C.DynamicBuffer{}

switch generatedType {
case FheBool:
ret := C.serialize_fhe_bool(resultPtr, ser)
if ret != 0 {
return nil, errors.New("GenerateObliviousPseudoRandom: serialization failed")
}
case FheUint4:
ret := C.serialize_fhe_uint4(resultPtr, ser)
if ret != 0 {
return nil, errors.New("GenerateObliviousPseudoRandom: serialization failed")
}
case FheUint8:
ret := C.serialize_fhe_uint8(resultPtr, ser)
if ret != 0 {
return nil, errors.New("GenerateObliviousPseudoRandom: serialization failed")
}
case FheUint16:
ret := C.serialize_fhe_uint16(resultPtr, ser)
if ret != 0 {
return nil, errors.New("GenerateObliviousPseudoRandom: serialization failed")
}
case FheUint32:
ret := C.serialize_fhe_uint32(resultPtr, ser)
if ret != 0 {
return nil, errors.New("GenerateObliviousPseudoRandom: serialization failed")
}
case FheUint64:
ret := C.serialize_fhe_uint64(resultPtr, ser)
if ret != 0 {
return nil, errors.New("GenerateObliviousPseudoRandom: serialization failed")
}
default:
return nil, fmt.Errorf("GenerateObliviousPseudoRandom: unsupported ciphertext type %d", generatedType)
}

defer C.destroy_dynamic_buffer(ser)
result.Serialization = C.GoBytes(unsafe.Pointer(ser.pointer), C.int(ser.length))
result.FheUintType = generatedType
result.computeHash()
return result, nil
}
56 changes: 56 additions & 0 deletions fhevm/tfhe/tfhe_wrappers.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ void* deserialize_fhe_uint8(DynamicBufferView in) {
return ct;
}


void* deserialize_compact_fhe_uint8(DynamicBufferView in) {
CompactFheUint8List* list = NULL;
FheUint8* ct = NULL;
Expand Down Expand Up @@ -426,6 +427,61 @@ void destroy_fhe_uint160(void* ct) {
assert(r == 0);
}

void* generate_oblivious_pseudo_random_uint4(uint64_t seed, uint64_t numberOfBits, void* sks) {
FheUint4* result = NULL;

checked_set_server_key(sks);

const int r = generate_oblivious_pseudo_random_bits_fhe_uint4(&result, seed, seed, numberOfBits);
if(r != 0) return NULL;

return result;
}

void* generate_oblivious_pseudo_random_uint8(uint64_t seed, uint64_t numberOfBits, void* sks) {
FheUint8* result = NULL;

checked_set_server_key(sks);

const int r = generate_oblivious_pseudo_random_bits_fhe_uint8(&result, seed, seed, numberOfBits);
if(r != 0) return NULL;

return result;
}

void* generate_oblivious_pseudo_random_uint16(uint64_t seed, uint64_t numberOfBits, void* sks) {
FheUint16* result = NULL;

checked_set_server_key(sks);

const int r = generate_oblivious_pseudo_random_bits_fhe_uint16(&result, seed, seed, numberOfBits);
if(r != 0) return NULL;

return result;
}

void* generate_oblivious_pseudo_random_uint32(uint64_t seed, uint64_t numberOfBits, void* sks) {
FheUint32* result = NULL;

checked_set_server_key(sks);

const int r = generate_oblivious_pseudo_random_bits_fhe_uint32(&result, seed, seed, numberOfBits);
if(r != 0) return NULL;

return result;
}

void* generate_oblivious_pseudo_random_uint64(uint64_t seed, uint64_t numberOfBits, void* sks) {
FheUint64* result = NULL;

checked_set_server_key(sks);

const int r = generate_oblivious_pseudo_random_bits_fhe_uint64(&result, seed, seed, numberOfBits);
if(r != 0) return NULL;

return result;
}

void* add_fhe_uint4(void* ct1, void* ct2, void* sks)
{
FheUint4* result = NULL;
Expand Down
10 changes: 10 additions & 0 deletions fhevm/tfhe/tfhe_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ void destroy_fhe_uint64(void* ct);

void destroy_fhe_uint160(void* ct);

void* generate_oblivious_pseudo_random_uint4(uint64_t seed, uint64_t numberOfBits, void* sks);

void* generate_oblivious_pseudo_random_uint8(uint64_t seed, uint64_t numberOfBits, void* sks);

void* generate_oblivious_pseudo_random_uint16(uint64_t seed, uint64_t numberOfBits, void* sks);

void* generate_oblivious_pseudo_random_uint32(uint64_t seed, uint64_t numberOfBits, void* sks);

void* generate_oblivious_pseudo_random_uint64(uint64_t seed, uint64_t numberOfBits, void* sks);

void* add_fhe_uint4(void* ct1, void* ct2, void* sks);

void* add_fhe_uint8(void* ct1, void* ct2, void* sks);
Expand Down

0 comments on commit f00a728

Please sign in to comment.