Пример #1
0
// setInitialSeed sets the initial seed for the Generator.  An
// attempt is made to obtain seeds which differ between machines and
// between reboots.  To achieve this, the following information is
// incorporated into the seed: the current time of day, account
// information for the current user, and information about the
// installed network interfaces.  In addition, if available, random
// bytes from the random number generator in the crypto/rand package
// are used.
func (gen *Generator) setInitialSeed() {
	// source 1: system random number generator
	buffer := make([]byte, keySize)
	n, _ := io.ReadFull(rand.Reader, buffer)
	if n > 0 {
		trace.T("fortuna/seed", trace.PrioInfo,
			"mixing %d bytes from crypto/rand into the seed", n)
		gen.Reseed(buffer)
	}

	// source 2: current time of day
	now := time.Now()
	trace.T("fortuna/seed", trace.PrioInfo,
		"mixing the current time into the seed")
	gen.Reseed(int64ToBytes(now.UnixNano()))

	// source 3: try different files with timer information, interrupt
	// counts, etc.
	for _, fname := range []string{"/proc/timer_list", "/proc/stat"} {
		buffer, _ = ioutil.ReadFile(fname)
		if len(buffer) > 0 {
			trace.T("fortuna/seed", trace.PrioInfo,
				"mixing %d bytes from %q into the seed", len(buffer), fname)
			gen.Reseed(buffer)
		}
	}

	// source 4: user name and login details
	user, _ := user.Current()
	if user != nil {
		trace.T("fortuna/seed", trace.PrioInfo,
			"mixing information about the current user into the seed")
		gen.Reseed([]byte(user.Uid))
		gen.Reseed([]byte(user.Gid))
		gen.Reseed([]byte(user.Username))
		gen.Reseed([]byte(user.Name))
		gen.Reseed([]byte(user.HomeDir))
	}

	// source 5: network interfaces
	ifaces, _ := net.Interfaces()
	if ifaces != nil {
		trace.T("fortuna/seed", trace.PrioInfo,
			"mixing network interface information into the seed")
		for _, iface := range ifaces {
			gen.ReseedInt64(int64(iface.MTU))
			gen.Reseed([]byte(iface.Name))
			gen.Reseed(iface.HardwareAddr)
			gen.ReseedInt64(int64(iface.Flags))
		}
	}
}
Пример #2
0
func (acc *Accumulator) tryReseeding() []byte {
	now := time.Now()

	acc.poolMutex.Lock()
	defer acc.poolMutex.Unlock()

	if acc.poolZeroSize >= minPoolSize && now.After(acc.nextReseed) {
		acc.nextReseed = now.Add(minReseedInterval)
		acc.poolZeroSize = 0
		acc.reseedCount++

		seed := make([]byte, 0, numPools*sha256d.Size)
		pools := []string{}
		for i := uint(0); i < numPools; i++ {
			x := 1 << i
			if acc.reseedCount%x != 0 {
				break
			}
			seed = acc.pool[i].Sum(seed)
			acc.pool[i].Reset()
			pools = append(pools, strconv.Itoa(int(i)))
		}
		trace.T("fortuna/seed", trace.PrioInfo,
			"reseeding from pools %s", strings.Join(pools, " "))
		return seed
	}
	return nil
}
Пример #3
0
// NewEntropyTimeStampSink returns a channel through which timing data
// can be submitted to the Accumulator's entropy pools.  The current
// time should be written to the returned channel regularly to add
// entropy to the state of the random number generator.  The submitted
// times should be chosen such that they cannot be (completely) known
// to an attacker.  Typical sources of randomness include the arrival
// times of network packets or the times of key-presses by the user.
//
// The channel can be closed by the caller to indicate that no more
// entropy will be sent via this channel.
func (acc *Accumulator) NewEntropyTimeStampSink() chan<- time.Time {
	source := acc.allocateSource()

	c := make(chan time.Time, channelBufferSize)

	acc.sources.Add(1)
	go func() {
		defer acc.sources.Done()
		seq := uint(0)
		lastRequest := time.Now()

	loop:
		for {
			select {
			case now, ok := <-c:
				if !ok {
					break loop
				}

				dt := now.Sub(lastRequest)
				lastRequest = now

				trace.T("fortuna/entropy", trace.PrioDebug,
					"adding time stamp data from source %d to pool %d",
					source, seq%numPools)
				acc.addRandomEvent(source, seq, int64ToBytes(int64(dt)))
				seq++
			case <-acc.stopSources:
				break loop
			}
		}
	}()

	return c
}
Пример #4
0
// Reseed uses the current generator state and the given seed value to
// update the generator state.  Care is taken to make sure that
// knowledge of the new state after a reseed does not allow to
// reconstruct previous output values of the generator.
//
// This is like the ReseedInt64() method, but the seed is given as a
// byte slice instead of as an int64.
func (gen *Generator) Reseed(seed []byte) {
	hash := sha256d.New()
	hash.Write(gen.key)
	hash.Write(seed)
	gen.setKey(hash.Sum(nil))
	gen.inc()
	trace.T("fortuna/generator", trace.PrioVerbose, "seed updated")
}
Пример #5
0
// Read and update the seed file.
//
// If the seed file is empty, reading the seed file is omitted.  After
// (potentially) reading the contents of the seed file, new seed data
// is written to the file.  In case the seed file is corrupted or has
// insecure file permissions, an error is returned.
func (acc *Accumulator) updateSeedFile() error {
	fi, err := acc.seedFile.Stat()
	if err != nil {
		return err
	} else if fi.Mode()&os.FileMode(0077) != 0 {
		trace.T("fortuna/seed", trace.PrioError,
			"seed file %q has insecure permissions, aborted",
			acc.seedFile.Name())
		return ErrInsecureSeed
	}

	_, err = acc.seedFile.Seek(0, os.SEEK_SET)
	if err != nil {
		return err
	}

	acc.genMutex.Lock()
	// To prevent attacks we keep the PRNG locked until the new seed
	// file is safely written to disk.
	defer acc.genMutex.Unlock()

	n := fi.Size()
	if n == seedFileSize {
		seed := make([]byte, seedFileSize)
		_, err := io.ReadFull(acc.seedFile, seed)
		if err != nil || isZero(seed) {
			trace.T("fortuna/seed", trace.PrioError,
				"seed file %q is corrupted, not used: %s",
				acc.seedFile.Name(), err)
			return ErrCorruptedSeed
		}
		trace.T("fortuna/seed", trace.PrioInfo,
			"mixing %q into the seed", acc.seedFile.Name())
		acc.gen.Reseed(seed)
	} else if n != 0 {
		trace.T("fortuna/seed", trace.PrioError,
			"seed file %q has invalid length %d, aborted",
			acc.seedFile.Name(), n)
		return ErrCorruptedSeed
	}

	seed := acc.randomDataUnlocked(seedFileSize)
	return doWriteSeed(acc.seedFile, seed)
}
Пример #6
0
func main() {
	trace.Register(printTrace, "", trace.PrioDebug)

	rng, err := fortuna.NewRNG(seedFileName)
	if err != nil {
		panic("cannot initialise the RNG: " + err.Error())
	}
	defer rng.Close()

	// entropy source 1: submit some randomness from crypto/rand once a minute
	go func() {
		sink1 := rng.NewEntropyDataSink()
		for _ = range time.Tick(time.Minute) {
			buffer := make([]byte, 4)
			n, _ := rand.Read(buffer)
			sink1 <- buffer[:n]
		}
	}()

	// entropy source 2: submit time between requests
	sink2 := rng.NewEntropyTimeStampSink()
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		sink2 <- time.Now()

		sizeStr := r.URL.Query().Get("len")
		size, _ := strconv.ParseInt(sizeStr, 0, 32)
		if size <= 0 {
			size = 16
		}
		w.Header().Set("Content-Length", fmt.Sprintf("%d", size))

		io.CopyN(w, rng, size)
		trace.T("main", trace.PrioInfo,
			"sent %d random bytes for %q", size, r.RequestURI)
	})

	listenAddr := ":8080"
	trace.T("main", trace.PrioInfo,
		"listening on http://localhost%s/", listenAddr)
	err = http.ListenAndServe(listenAddr, nil)
	if err != nil {
		trace.T("main", trace.PrioCritical, "%s", err.Error())
	}
}
Пример #7
0
// PseudoRandomData returns a slice of n pseudo-random bytes.  The
// result can be used as a replacement for a sequence of n uniformly
// distributed and independent bytes.
func (gen *Generator) PseudoRandomData(n uint) []byte {
	numBlocks := gen.numBlocks(n)
	res := make([]byte, 0, numBlocks*uint(len(gen.counter)))

	for numBlocks > 0 {
		count := numBlocks
		if count > maxBlocks {
			count = maxBlocks
		}
		res = gen.generateBlocks(res, count)
		numBlocks -= count

		newKey := gen.generateBlocks(nil, gen.numBlocks(keySize))
		gen.setKey(newKey[:keySize])
	}

	trace.T("fortuna/generator", trace.PrioVerbose,
		"generated %d pseudo-random bytes", n)
	return res[:n]
}
Пример #8
0
func doWriteSeed(f *os.File, seed []byte) error {
	_, err := f.Seek(0, os.SEEK_SET)
	if err != nil {
		return err
	}

	n, err := f.Write(seed)
	if err != nil || n != len(seed) {
		if err == nil {
			err = &os.PathError{Op: "write", Path: f.Name(), Err: nil}
		}
		return err
	}

	err = f.Sync()
	if err != nil {
		return err
	}

	trace.T("fortuna/seed", trace.PrioInfo,
		"writing new seed data to %q", f.Name())
	return nil
}
Пример #9
0
// NewEntropyDataSink returns a channel through which data can be
// submitted to the Accumulator's entropy pools.  Data should be
// written to the returned channel periodically to add entropy to the
// state of the random number generator.  The written data should be
// derived from quantities which change between calls and which cannot
// be (completely) known to an attacker.  Typical sources of
// randomness include noise from a microphone/camera, CPU cycle
// counters, or the number of processes running on the system.
//
// If the data written to the channel is longer than 32 bytes, the
// data is hashed internally and the hash is submitted to the entropy
// pools instead of the data itself.
//
// The channel can be closed by the caller to indicate that no more
// entropy will be sent via this channel.
func (acc *Accumulator) NewEntropyDataSink() chan<- []byte {
	source := acc.allocateSource()

	c := make(chan []byte, channelBufferSize)

	acc.sources.Add(1)
	go func() {
		defer acc.sources.Done()
		seq := uint(0)

	loop:
		for {
			select {
			case data, ok := <-c:
				if !ok {
					break loop
				}

				if len(data) > 32 {
					hash := sha256.New()
					hash.Write(data)
					data = hash.Sum(nil)
				}

				trace.T("fortuna/entropy", trace.PrioDebug,
					"adding %d bytes from source %d to pool %d",
					len(data), source, seq%numPools)
				acc.addRandomEvent(source, seq, data)
				seq++
			case <-acc.stopSources:
				break loop
			}
		}
	}()

	return c
}