Skip to content
This repository was archived by the owner on Feb 2, 2022. It is now read-only.

Fixed index out of range panic#9

Closed
hakonra wants to merge 1 commit intocapitalone:masterfrom
Videxio:master
Closed

Fixed index out of range panic#9
hakonra wants to merge 1 commit intocapitalone:masterfrom
Videxio:master

Conversation

@hakonra
Copy link

@hakonra hakonra commented Oct 9, 2017

What's in this PR?

Fix for panic in ff1 that occurred when encrypting certain numeric strings (which ones depend on the given tweak). It may also have been an issue for other types of strings, but that has not been checked.
The fix has been verified for all 9-digit numbers (000000001-999999999).

Housekeeping Items

@anitgandhi anitgandhi closed this Oct 10, 2017
@anitgandhi anitgandhi reopened this Oct 10, 2017
@anitgandhi
Copy link
Contributor

@hakonra The Travis CI builds aren't getting triggered but I ran go test github.com/Videxio/fpe/ff1 and all unit test cases are failing, which does make sense given the change introduced here.

Can you please provide some combinations of inputs/tweaks that reproduce this issue?

@hakonra
Copy link
Author

hakonra commented Oct 11, 2017

You can try to run the program below (it is what I wrote to verify).
It should panic very quickly without the fix, and with the fix I managed to run the entire test without errors. It also verifies that all expected numbers are indeed generated.

package main

import (
	"github.com/capitalone/fpe/ff1"

	"fmt"
	"strconv"
	"sync"
	"time"
)

const (
	MIN = 1
	MAX = 999999999
)

func main() {
	key := []byte{0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x4, 0x8, 0x15, 0x16, 0x23, 0x42}
	tweak := []byte{0x42}

	bitmapMutex := sync.Mutex{}
	bitmapLen := (MAX + 1) / 8
	if (MAX+1)%8 > 0 {
		bitmapLen++
	}
	fmt.Printf("[%s] Allocating bitmap of size %d bytes...\n", time.Now().String(), bitmapLen)
	bitmap := make([]byte, bitmapLen)

	workers := []uint32{250000000, 500000000, 750000000, MAX}
	wg := sync.WaitGroup{}
	wg.Add(len(workers))

	fmt.Printf("[%s] Starting %d workers...\n", time.Now().String(), len(workers))
	for gr := 0; gr < len(workers); gr++ {
		min := uint32(MIN)
		if gr > 0 {
			min = workers[gr-1] + 1
		}
		max := workers[gr]

		go func(gr int) {
			cipher, err := ff1.NewCipher(10, 8, key, tweak)
			if err != nil {
				fmt.Printf("[CRITICAL] ff1.NewCipher(): %s\n", err.Error())
				return
			}

			var i uint32
			percent := 0
			perCalc := float64(max-min+1) / 100.0
			for i = min; i <= max; i++ {
				if int(float64(i-min)/perCalc) > percent {
					percent++
					fmt.Printf("[%s -- worker %d] %d%%\n", time.Now().String(), gr, percent)
				}

				ciphertext, err := cipher.Encrypt(fmt.Sprintf("%09d", i))
				if err != nil {
					fmt.Println(err.Error())
				} else {
					c, err := strconv.Atoi(ciphertext)
					if err != nil {
						fmt.Printf("strconv.Atoi(): %s\n", err.Error())
						continue
					}

					bitmapMutex.Lock()
					if get(bitmap, c) {
						bitmapMutex.Unlock()
						fmt.Printf("[%s -- worker %d] Two equal:  %d -> %s  ==  ? -> %s\n", time.Now().String(), gr, i, ciphertext, ciphertext)
					} else {
						set(bitmap, c)
						bitmapMutex.Unlock()
					}
				}
			}

			wg.Done()
			fmt.Printf("[%s -- worker %d] Finished!\n", time.Now().String(), gr)
		}(gr)
	}

	wg.Wait()

	fmt.Printf("\n[%s] main verifying...\n", time.Now().String())
	matches := 0
	for i := 0; i <= MAX; i++ {
		if get(bitmap, int(i)) {
			matches++
		}
	}

	total := int(MAX - MIN + 1)

	if matches == total {
		fmt.Printf("GREAT SUCCESS!\n\n")
	} else {
		fmt.Printf("Generated %d numbers of %d total :(\n\n", matches, total)
	}
}

func get(bitmap []byte, key int) bool {
	return (bitmap[key/8]&getMask(key%8) > 0)
}

func set(bitmap []byte, key int) {
	bitmap[key/8] |= getMask(key % 8)
}

func getMask(mod int) byte {
	switch mod {
	case 0:
		return 0x01 // 00000001
	case 1:
		return 0x02 // 00000010
	case 2:
		return 0x04 // 00000100
	case 3:
		return 0x08 // 00001000
	case 4:
		return 0x10 // 00010000
	case 5:
		return 0x20 // 00100000
	case 6:
		return 0x40 // 01000000
	case 7:
		return 0x80 // 10000000
	default:
		panic(fmt.Sprintf("shit: %d", mod))
	}
}

@anitgandhi
Copy link
Contributor

After some investigation, it turns out this is actually an issue with plaintext strings that have enough trailing zeros, such that numB.Bytes() just returns an empty slice.

Will push a fix that doesn't break existing test vectors / unit tests.

@anitgandhi
Copy link
Contributor

@hakonra the fix has been merged via #11 . Re-ran your test program and it no longer panics.

Thanks for raising this issue!

@anitgandhi anitgandhi closed this Oct 11, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants