Skip to content

Instantly share code, notes, and snippets.

@ixe013
Last active November 23, 2019 01:14
Show Gist options
  • Save ixe013/bde72b20d187e3e249505e58404b8f73 to your computer and use it in GitHub Desktop.
Save ixe013/bde72b20d187e3e249505e58404b8f73 to your computer and use it in GitHub Desktop.
Go code used to play around FPE / FF1 asking this question https://crypto.stackexchange.com/q/75926/1949
package main
import (
"encoding/hex"
"fmt"
"strings"
"github.com/capitalone/fpe/ff1"
//"alphabets"
)
// I'll be damned: https://stackoverflow.com/a/27516559/591064
func min(a, b int) int {
if a < b {
return a
}
return b
}
func generateCharactersOfBase(length int) string {
chars := make([]rune, length)
i := 0
soFar := 0
//Start with digits
for ; i < min(10, length); i++ {
chars[i] = rune(int('0') + i)
}
soFar = i
//Then with uppercase letters
for ; i < min(26+soFar, length); i++ {
chars[i] = rune(int('a') + i - soFar)
}
soFar = i
//Now with lowercase letters
for ; i < min(26+soFar, length); i++ {
chars[i] = rune(int('A') + i - soFar)
}
return string(chars)
}
type Alphabet struct {
characterSet string
baseXDigits string
}
func (alphabet *Alphabet) Length() int {
return len(alphabet.characterSet)
}
func NewAlphabet(characterSet string) *Alphabet {
a := new(Alphabet)
a.characterSet = characterSet
a.baseXDigits = generateCharactersOfBase(len(characterSet))
return a
}
func (alphabet *Alphabet) Convert(text string) string {
/* Equivalent Python
>>> import string
>>> base36 = list(string.digits+string.ascii_uppercase[:26-10])
>>> ''.join([base36[string.ascii_uppercase.index(c)] for c in 'ABCHELLO'])
'01274BBE'
//*/
chars := make([]rune, len(text))
for i := 0; i < len(chars); i++ {
pos := strings.Index(alphabet.characterSet, string(text[i]))
chars[i] = rune(alphabet.baseXDigits[pos])
}
return string(chars)
}
func (alphabet *Alphabet) Revert(text string) string {
/* Equivalent Python
>>> import string
>>> base36 = list(string.digits+string.ascii_uppercase[:26-10])
>>> ''.join([base36[string.ascii_uppercase.index(c)] for c in 'ABCHELLO'])
'01274BBE'
//*/
chars := make([]rune, len(text))
for i := 0; i < len(chars); i++ {
pos := strings.Index(alphabet.baseXDigits, string(text[i]))
chars[i] = rune(alphabet.characterSet[pos])
}
return string(chars)
}
func encryptFF1(key []byte, tweak []byte, plaintext string, alphabet *Alphabet ) string {
basedPlaintext := alphabet.Convert(plaintext)
// Create a new FF1 cipher "object"
// 10 is the radix/base, and 8 is the tweak length.
FF1, err := ff1.NewCipher(alphabet.Length(), len(tweak), key, tweak)
if err != nil {
panic(err)
}
// Call the encryption function on an example SSN
ciphertext, err := FF1.Encrypt(basedPlaintext)
if err != nil {
panic(err)
}
//Move the result to the original alphabet
return alphabet.Revert(ciphertext)
}
func decryptFF1(key []byte, tweak []byte, ciphertext string, alphabet *Alphabet ) string {
basedCiphertext := alphabet.Convert(ciphertext)
// Create a new FF1 cipher "object"
// 10 is the radix/base, and 8 is the tweak length.
FF1, err := ff1.NewCipher(alphabet.Length(), len(tweak), key, tweak)
if err != nil {
panic(err)
}
// Call the encryption function on an example SSN
plaintext, err := FF1.Encrypt(basedCiphertext)
if err != nil {
panic(err)
}
//Move the result to the original alphabet
return alphabet.Revert(plaintext)
}
// panic(err) is just used for example purposes.
func main() {
// Key and tweak should be byte arrays. Put your key and tweak here.
// To make it easier for demo purposes, decode from a hex string here.
key, err := hex.DecodeString("EF4359D8D580AA4F7F036D6F04FC6A94")
if err != nil {
panic(err)
}
tweak, err := hex.DecodeString("D8E7920AFA330A73")
tweak = nil
if err != nil {
panic(err)
}
a := NewAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
ciphertext := encryptFF1(key, tweak, "HELLO", a)
plaintext := decryptFF1(key, tweak, ciphertext, a)
fmt.Println("Ciphertext:", ciphertext)
fmt.Println("Plaintext:", plaintext)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment