Swift RC4 vs. Objective-C RC4 Performance

1.2k Views Asked by At

I have been trying to rewrite a Rc4-algorithm from objective-c to swift, to test out apples(now old) claims, about it running a lot faster. However there must be somewhere that I am doing something horribly wrong with these times I am getting

This is the objective c code:

+(NSString*)Rc4:(NSString*)aInput key:(NSString *)aKey {
    NSMutableArray *iS = [[NSMutableArray alloc] initWithCapacity:256];
    NSMutableArray *iK = [[NSMutableArray alloc] initWithCapacity:256];

    for (int i = 0; i <256;i++){
        [iS addObject:[NSNumber numberWithInt:i]];
    }

    for(short i=0;i<256;i++){
        UniChar c = [aKey characterAtIndex:i%aKey.length];
        [iK addObject:[NSNumber numberWithChar:c]];
    }
    int j=2;
    for (int i=0; i<255;i++){
        int is = [[iS objectAtIndex:i]  intValue];
        UniChar ik = (UniChar)[[iK objectAtIndex:i]charValue];

        j= (j+is+ik)%256;
        NSNumber *temp = [iS objectAtIndex:i];
        [iS replaceObjectAtIndex:i withObject:[iS objectAtIndex:j]];
        [iS replaceObjectAtIndex:j withObject:temp];
    }

    int i =0;
    j=0;
    NSString *result = aInput;

    for (short x=0;x<[aInput length]; x++){
        i = (i+1)%256;

        int is = [[iS objectAtIndex:i]intValue];
        j=(j+is)%256;

        int is_i = [[iS objectAtIndex:i]intValue];
        int is_j = [[iS objectAtIndex:j]intValue];

        int t= (is_i+is_j)%256;
        int iY = [[iS objectAtIndex:t]intValue];

        UniChar ch = (UniChar)[aInput characterAtIndex:x];
        UniChar ch_y=ch^iY;
        //NSLog(ch);
        //NSLog(iY);

        result = [result stringByReplacingCharactersInRange:NSMakeRange(x,1) withString:
                  [NSString stringWithCharacters:&ch_y length:1] ];
    }
    [iS release];
    [iK release];
    return result;
}

This runs pretty fast compiling with -O3 I get times of:

100 runs:0.006 seconds

With key: 6f7e2a3d744a3b5859725f412f (128bit)

and input: "MySecretCodeToBeEncryptionSoNobodySeesIt"

This is my attempt to implement it in the same way using Swift:

extension String {
   subscript (i: Int) -> String {
      return String(Array(self)[i])
   }
}
extension Character {
  func unicodeValue() -> UInt32 {
    for s in String(self).unicodeScalars {
      return s.value
    }
    return 0

    }
  }
func Rc4(input:String, key:String)-> String{
  var iS = Array(count:256, repeatedValue: 0)
  var iK = Array(count:256, repeatedValue: "")
  var keyLength = countElements(key)

  for var i = 0; i < 256; i++ {
    iS[i] = i;
  }

  for var i = 0; i < 256 ; i++ {
    var c = key[i%keyLength]
    iK[i] = c;  
  }

  var j = 2

  for var i = 0; i < 255; i++ {
    var iss = iS[i]
    var ik = iK[i]
    // transform string to int
    var ik_x:Character = Character(ik)
    var ikk_xx = Int(ik_x.unicodeValue())

    j = (j+iss+ikk_xx)%256;
    var temp = iS[i]
    iS[i] = iS[j]
    iS[j] = temp
  }

  var i = 0
  j=0
  var result = input
  var eles = countElements(input)
  for var x = 0 ; x<eles ; x++ {
    i = (i+1)%256

    var iss = iS[i]
    j = (j+iss)%256

    var is_i = iS[i]
    var is_j = iS[j]
    var t = (is_i+is_j)%256
    var iY = iS[t]

    var ch = (input[x])
    var ch_x:Character = Character(ch)
    var ch_xx = Int(ch_x.unicodeValue())
    var ch_y = ch_xx^iY
    var start = advance(result.startIndex, x)
    var end = advance(start,1);
    let range = Range(start:start, end:end)
    var maybestring = String(UnicodeScalar(ch_y))
    result = result.stringByReplacingCharactersInRange(range, withString:maybestring)
  }
  return result;
}

I have tried to implement it so it looks as much as the objective-c version as possible. This however gives me these horrible times, using -O

100 runs: 0.5 seconds

EDIT Code should now run in xcode 6.1 using the extension methods I posted.

I run it from terminal like this:

xcrun swiftc -O Swift.swift -o swift

where Swift.swift is my file, and swift is my executable

2

There are 2 best solutions below

1
On

Usually claims of speed don't really apply to encryption algorithms, they are more for what I usually call "business logic". The functions on bits, bytes, 16/32/64 bit words etc. are usually difficult to optimize. Basically encryption algorithms are designed to be dense operations on these data structures with relatively few choices that can be optimized away.

Take for instance Java. Although infinitely faster than most interpreted languages it really doesn't compare well with C/C++, let alone with assembly optimized encryption algorithms. The same goes for most relatively small algebraic problems.

To make things faster you should at least use explicit numeric types for your numbers.

2
On

After excessive testing of the code, i have narrowed it down to what is making my times ultra slow. If i comment out this code, so that the iK array just contains its initial value. i go from a runtime of 5 seconds to 1 second. Which is a significant increase.

for var i = 0; i < 256 ; i++ {
var c = key[i%keyLength]
iK[i] = c;  
}

The problem is with this part:

var c = key[i%keyLength]

There is no "characterAtIndex(int)" method in Swift, therefore i do this as a workaround to get the characterAtIndex. I do it using my extension:

extension String {
  subscript (i: Int) -> String {
    return String(Array(self)[i])
  }
}

But essentially it is the same as this:

var c = Array(key)[i%keyLength]

Instead of the O(1) - (constant time) of this operation in objective-c, we are getting a running time of O(n).