Class: FFXCodec::Encrypt

Inherits:
Object
  • Object
show all
Defined in:
lib/ffxcodec/encrypt.rb

Overview

Note:

WARNING: This was cooked up as an experimental proof of concept. It hasn’t been tested thoroughly and shouldn’t be considered secure.

Note:

Format-preserving != integer-size-preserving in base 10 (see below)

Implementation of AES-FFX mode format-preserving encryption

Cipher device encrypts integers where the resulting ciphertext has the same number of digits in the given base (radix).

The format-preserving characteristic of this cipher is best thought of as preserving the number of digits, not the integer size. For instance, in base 10, 4294967295 and 4294967296 would be considered to have the same format, but the first is a 32-bit unsigned integer and the second is 64.

So given base 10 input that fits within a 32 or 64-bit integer, it’s possible for the AES-FFX cipher to return a number that contains the same number of base 10 digits but exceeds the largest number that can be represented in 32 or 64 bits respectively.

You can work around this by using radix 2 so that the cipher returns an equal number of bits. As with all modes, you must supply input as a stringified integer in the base you’ve specified.

Be aware that when you convert between bases, leading zeros are sometimes dropped by the converter. You must supply the same number of digits to the decrypter as you did to the encrypter or you’ll get a different value. The encrypt and decrypt methods prepend zeros until the input is is of the length specified during initialization.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, tweak, length, radix = 10) ⇒ Encrypt

Returns a new instance of Encrypt.

Parameters:

  • key (String)

    for AES as a hexadecimal string

  • tweak (String)

    for AES

  • length (Fixnum)

    of the input

  • radix (Fixnum) (defaults to: 10)

    of the input



50
51
52
53
54
55
56
# File 'lib/ffxcodec/encrypt.rb', line 50

def initialize(key, tweak, length, radix = 10)
  self.key   = key
  self.tweak = tweak
  self.radix = radix
  @length    = length
  @rounds    = 10
end

Instance Attribute Details

#lengthObject

Parameters:

  • length (Fixnum)

    of input



36
37
38
# File 'lib/ffxcodec/encrypt.rb', line 36

def length
  @length
end

#radixFixnum

Returns radix of the input.

Returns:

  • (Fixnum)

    radix of the input



44
45
46
# File 'lib/ffxcodec/encrypt.rb', line 44

def radix
  @radix
end

#roundsObject

Note:

This is set to 10 by the spec. Don’t change it unless you know what you’re doing.

Parameters:

  • rounds (Fixnum)

    of encryption / decryption to run input through



41
42
43
# File 'lib/ffxcodec/encrypt.rb', line 41

def rounds
  @rounds
end

Instance Method Details

#decrypt(input) ⇒ Fixnum, Bignum

Decrypt

Examples:

Decrypt

e = Encrypt.new("4fb450a9c27dd07f22ef56413432c94a", "FZNT4F22E5QA5QUM")
e.decrypt(1224011974)  #=> "1234567890"

Parameters:

  • input (String)

    encrypted, stringifed integer of base @radix

Returns:

  • (Fixnum, Bignum)

    unencrypted integer



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/ffxcodec/encrypt.rb', line 106

def decrypt(input)
  a, b = input.prepad_zeros(@length).bisect
  (@rounds - 1).downto(0) do |iter|
    c = b
    b = a
    f = feistel_round(input.size, iter, b)
    lmin  = [c.size, f.size].min
    a = block_subtraction(lmin, c, f)
  end
  a + b
end

#encrypt(input) ⇒ Fixnum, Bignum

Encrypt

Examples:

Encrypt

e = Encrypt.new("4fb450a9c27dd07f22ef56413432c94a", "FZNT4F22E5QA5QUM")
e.encrypt(1234567890)  #=> "1224011974"

Parameters:

  • input (String)

    unencrypted, stringifed integer of base @radix

Returns:

  • (Fixnum, Bignum)

    encrypted integer



86
87
88
89
90
91
92
93
94
95
# File 'lib/ffxcodec/encrypt.rb', line 86

def encrypt(input)
  a, b = input.prepad_zeros(@length).bisect
  0.upto(@rounds - 1) do |iter|
    f = feistel_round(input.size, iter, b)
    c = block_addition(a, f)
    a = b
    b = c
  end
  a + b
end

#key=(key) ⇒ Object

Parameters:

  • key (String)

    for AES as a hexadecimal string



59
60
61
62
63
# File 'lib/ffxcodec/encrypt.rb', line 59

def key=(key)
  hexkey = [key].pack('H*')
  fail ArgumentError, "key must be a 16-byte hexidecimal" if hexkey.length != 16
  @key = hexkey
end

#tweak=(tweak) ⇒ Object

Parameters:

  • tweak (String)

    tweak for AES



66
67
68
69
# File 'lib/ffxcodec/encrypt.rb', line 66

def tweak=(tweak)
  fail ArgumentError, "tweak length must be under (2^32) - 1" if tweak.length > ((1 << 32) - 1)
  @tweak = tweak
end