1

I'm currently pulling my hair out trying to generate an info_hash from a torrent file I've downloaded. I created the Torrent file using uTorrent and using PeerTracker as my tracking software.

I'm using an updated version of the code found here:
https://code.google.com/p/rouge-server/source/browse/trunk/driver/objective-c/RougeDriver/BEncoding.m?r=88

{
    announce = "http://sub.url.com:8080/announce.php";
    "created by" = "uTorrent/1870";
    "creation date" = 1425134140;
    encoding = "UTF-8";
    info =     {
        length = 4;
        name = "test.txt";
        "piece length" = 16384;
        pieces = "\U00a9J\U00e8\U00c2\U00c3\U00b1\U00f5\U00b6L\bs\U201d\U00eb\U00c8\U00e1\U00f2/\U00aa\U201d";
    };
}

Below is the exact encoding method I'm using:

+ (void)encode:(id)object toString:(NSMutableString *)string
{
    if([object isKindOfClass:[NSDictionary class]])
    {
        NSDictionary *dictionary = (NSDictionary *)object;
        [string appendString:@"d"];

        for(id key in [dictionary allKeys])
        {
            [self encode:key toString:string];
            [self encode:[dictionary objectForKey:key] toString:string];
        }

        [string appendString:@"e"];
    }
    else if([object isKindOfClass:[NSString class]])
    {
        NSString *stringObject = (NSString *)object;
        NSString *format = @"%lu:%@";

        [string appendFormat:format, [string length], stringObject];
    }
    else if([object isKindOfClass:[NSArray class]])
    {
        NSArray *array = (NSArray *)object;
        [string appendString:@"l"];

        for(id item in array)
        {
            [self encode:item toString:string];
        }

        [string appendString:@"e"];
    }
    else if([object isKindOfClass:[NSNumber class]])
    {
        NSNumber *number = (NSNumber *)object;
        NSString *format = @"i%de";

        [string appendFormat:format, [number intValue]];
    }
}

This is my first time working with Torrents and bencoding. I know I'm only supposed to Encode the "info" dictionary contained within the main dictionary.

Does anyone one know where I could be going wrong here?

Pli_mo
  • 53
  • 2
  • 6
  • `pieces` does not seem to be UTF-8 but a mix including UTF-16: "\U00a9" seems to be the UTF-16 character encoding for "©". – zaph Feb 28 '15 at 15:18
  • What would I need to delete that? Is it in the decoding? or encoding? – Pli_mo Feb 28 '15 at 16:42
  • 1
    I did not say you needed to delete anything. In the question: "encoding = "UTF-8" but many of the characters seem to be UTF-16. There seems to be a mismatch. – zaph Feb 28 '15 at 17:14
  • At the moment I'm downloading the file and opening it with NSData dataFromContentsOfUrl. To test I've now created torrents with both BitTorrent and uTorrent. Both are claiming UTF-8 for encoding but both have the "©" at the beginning of the pieces section. – Pli_mo Feb 28 '15 at 22:46
  • If you are downloading and have `NSData`, log and post that. I suspect that the way you are displaying `pieces` is misleading. – zaph Feb 28 '15 at 23:35
  • Thanks for looking into this. Here's the complete loaded NSData from file: <64383a61 6e6e6f75 6e636534 363a6874 74703a2f 2f737465 616d2e76 6964656f 67616d65 6875622e 636f3a38 3038302f 616e6e6f 756e6365 2e706870 31303a63 72656174 65642062 7931363a 42697454 6f727265 6e742f37 2e392e32 31333a63 72656174 696f6e20 64617465 69313432 35313633 33303565 383a656e 636f6469 6e67353a 5554462d 38343a69 6e666f64 363a6c65 6e677468 69346534 3a6e616d 6531303a 6d797465 73742e74 78743132 3a706965 6365206c 656e6774 68693136 33383465 363a7069 65636573 32303aa9 4a8fe5cc b19ba61c 4c0873d3 91e98798 2fbbd365 65> – Pli_mo Feb 28 '15 at 23:46
  • 1
    It is only anything text that is UTF-8 encoded in a torrent file. The `pieces` data is a byte string of the raw 20 byte binary SHA1 hashes of the piece data concatenated together after each other. – Encombe Mar 01 '15 at 00:25

2 Answers2

1

The actual pieces content is data:

0xa9, 0x4a, 0x8f, 0xe5, 0xcc, 0xb1, 0x9b, 0xa6, 0x1c, 0x4c, 0x08, 0x73, 0xd3, 0x91, 0xe9, 0x87, 0x98, 0x2f, 0xbb, 0xd3

As @Encombe states this is the SHA1 hash.

From Wikipedia:

The specification does not deal with encoding of characters outside the ASCII set; to mitigate this, some BitTorrent applications explicitly communicate the encoding (most commonly UTF-8) in various non-standard ways.

And pieces is hex data, not UTF-8.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • But aren't I meant to encode the entire "info" dictionary to generate the info_hash I can announce? Or is there something special I need to do with this piece data before encoding the info dictionary? – Pli_mo Mar 01 '15 at 00:36
  • The wikipedia article is a bit outdated. [The BitTorrent Protocol Specification - BEP3](http://bittorrent.org/beps/bep_0003.html) has been updated and now explicitly states UTF-8. – Encombe Mar 01 '15 at 00:40
  • Maybe this answer can help: http://stackoverflow.com/questions/19749085/calculating-the-info-hash-of-a-torrent-file/19800109#19800109 – Encombe Mar 01 '15 at 01:05
  • @Encombe My first comment was that it included UTF-16. The `pieces` data is **not** UTF-8. The displayed `pieces` value is incorrect, see the data in this answer. – zaph Mar 01 '15 at 01:12
  • Thanks guys, I've followed the directions exactly in the SO answer linked. I'm now hashing the info without actually decoding it (just calculating the start and end points and hashing everything in between). However, I'm still getting an incorrect hash. If I use an online torrent file inspector it is calculating the correct hash from these exact files – Pli_mo Mar 01 '15 at 01:58
  • @Pli_mo Did you include the `d` i the beginning and the `e` in the end of the hashed string? – Encombe Mar 01 '15 at 02:05
  • Yep is includes both the starting d and the ending e – Pli_mo Mar 01 '15 at 02:10
  • 1
    Thank you guys so much for your help. It must have been something to do with the encoding. I've now re-followed the instructions in your link Encombe and instead of saving to an NSString or anything I literally just append each byte of the info dictionary to an NSMutableData instance and then SHA1 that. This gives the correct result every time. I don't have to worry about encoding as it has the same encoding as it did in the main torrent file. Thanks again! – Pli_mo Mar 01 '15 at 19:32
  • @Pli_mo Good, I think you should post your solution as an answer, so that anyone more with this problem can see it. – Encombe Mar 01 '15 at 23:19
1

I finally found the solution to this issue. As everyone suspected it was an encoding issue.

I followed the instructions from this SO: Calculating the info-hash of a torrent file

However, I made one small tweak:

+ (NSDictionary *)decodeDictionary:(HTData *)data
{
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

    [data getNextCharacter];

    while([data showNextCharacter] != 'e')
    {
        NSString *key = [self decodeString:data];

        id object;

        if([key isEqualToString:@"info"])
        {
            NSInteger startIndex = [data getCurrentIndex];
            NSInteger endIndex;
            object = [self decodeNextObject:data];

            endIndex = [data getCurrentIndex];

            NSMutableData *charData = [[NSMutableData alloc] init];

            for(NSInteger i = startIndex; i < endIndex; i++)
            {
                char c = [data charAtIndex:i];
                [charData appendBytes:&c length:1];
            }

            NSData *hashedInfo = [charData SHA1];

            [dictionary setObject:hashedInfo forKey:@"info_hash"];
        }
        else
        {
            object = [self decodeNextObject:data];
        }

        [dictionary setObject:object forKey:key];
    }

    [data getNextCharacter];

    return dictionary;
 }

I'm now calculating the hash within the dictionary decoder. I'm also working with individual chars and appending them directly to an NSMutableData object. This avoids the encoding issues I had and generates the correct hash.

Not the most elegant solution but I'm just glad it works.

Community
  • 1
  • 1
Pli_mo
  • 53
  • 2
  • 6