Home > Mobile >  SWIFT - JSON Error NSCocoaErrorDomain Code=3840 "Garbage at end."
SWIFT - JSON Error NSCocoaErrorDomain Code=3840 "Garbage at end."

Time:09-26

I have below function where I try to decode(base64), decrypt and create JSON dictionary,

However, I get an error called NSCocoaErrorDomain Code=3840 "Garbage at end." for some unknown reason decryption creates \0\0\0\0\0\0 at the end of the JSON string (probably padding). I am using CryptoSwift to decrypt the response. I am unable to find a way to make this works, as should be pretty straight forward but i am missing something important step in my code.

import CryptoSwift
func orientation() -> Observable<AnyObject?> {
        return Observable<AnyObject?>.create({ (observer) -> Disposable in
            
            let request = Alamofire.Session.default.request(ResourcePath.Orientation.path "2", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: ["x-remote-origin":"ios"]).responseJSON(completionHandler: { (dataResponse) in
                switch (dataResponse.result) {
                case .success(let value) :
                    print(value)
                    do {
                        
                        let json = JSON(value)
                        if let response = json.dictionary {
                            //Now you got your value                                
                            let result = response["response"]?.string;
                            
                            let iv="something".bytes;
                            /* AES cryptor instance */
                            let aes = try AES(key: self.keyForCrypting, blockMode: CBC(iv: iv))
                            
                            let encryptedData = Data(base64Encoded:result!)!
                            let decryptedData = Data(try aes.decrypt(encryptedData.bytes))
                            print(decryptedData);
                            let decryptedText = String(data: decryptedData, encoding: .utf8)
                            print(decryptedText);
                            let jsonData = try JSON(data: decryptedData)
                            print(jsonData);
                        }
                       
                    }
                    catch {
                        print(error);
                        observer.onError(error)
                        return
                    }
                    break
                case .failure(let error) :
                    
                    observer.onError(error)
                    break
                }
            })
            
            return Disposables.create {
                 
                request.cancel()
            }
        })
    }

My JSON output ( print(decryptedText); ) below.

{\"oriens\":[{\"id\":\"1\",\"title\":\"Im Groom seeking a Bride\",\"search\":\"1\",\"gender\":\"M\",\"free\":\"N\",\"container\":{},\"dirtyState\":0,\"dirtyRelated\":[],\"errorMessages\":[],\"modelsManager\":{},\"modelsMetaData\":null,\"related\":[],\"operationMade\":0,\"oldSnapshot\":[],\"skipped\":null,\"snapshot\":null,\"transaction\":null,\"uniqueKey\":null,\"uniqueParams\":null,\"uniqueTypes\":null},{\"id\":\"2\",\"title\":\"Im Bride seeking a Groom\",\"search\":\"2\",\"gender\":\"F\",\"free\":\"Y\",\"container\":{},\"dirtyState\":0,\"dirtyRelated\":[],\"errorMessages\":[],\"modelsManager\":{},\"modelsMetaData\":null,\"related\":[],\"operationMade\":0,\"oldSnapshot\":[],\"skipped\":null,\"snapshot\":null,\"transaction\":null,\"uniqueKey\":null,\"uniqueParams\":null,\"uniqueTypes\":null},{\"id\":\"3\",\"title\":\"Im Boy seeking a Girl\",\"search\":\"3\",\"gender\":\"M\",\"free\":\"N\",\"container\":{},\"dirtyState\":0,\"dirtyRelated\":[],\"errorMessages\":[],\"modelsManager\":{},\"modelsMetaData\":null,\"related\":[],\"operationMade\":0,\"oldSnapshot\":[],\"skipped\":null,\"snapshot\":null,\"transaction\":null,\"uniqueKey\":null,\"uniqueParams\":null,\"uniqueTypes\":null},{\"id\":\"4\",\"title\":\"Im Girl seeking a Boy\",\"search\":\"4\",\"gender\":\"F\",\"free\":\"Y\",\"container\":{},\"dirtyState\":0,\"dirtyRelated\":[],\"errorMessages\":[],\"modelsManager\":{},\"modelsMetaData\":null,\"related\":[],\"operationMade\":0,\"oldSnapshot\":[],\"skipped\":null,\"snapshot\":null,\"transaction\":null,\"uniqueKey\":null,\"uniqueParams\":null,\"uniqueTypes\":null}]}\0\0\0\0\0\0

My XCode output is

enter image description here

CodePudding user response:

Here's a short extension over Data for removing the trailing zeros:

extension Data {
    func removingTrailingZeros() -> Data {
        guard !isEmpty else { return self }
        
        var lastValidIndex = index(before: endIndex)
        while self[lastValidIndex] == 0 { lastValidIndex = index(before:  lastValidIndex)}
        
        return self[startIndex...lastValidIndex]
    }
}

, then in your code you can use it as decryptedData.removingTrailingZeros().

Note that removingTrailingZeros() doesn't actually remove the zeros, what it does is it creates a new Data that projects over the bytes of the source one, but only the ones before the first zero (or until the end, if there are no trailing zeros).

CodePudding user response:

I was able to get it somehow to work in one of the weirdest ways, surely there is a better way to do this, Just posting here in case someone can add a better more correct code to fix this issue as the Answer.

func orientation() -> Observable<AnyObject?> {
    return Observable<AnyObject?>.create({ (observer) -> Disposable in
        let request = Alamofire.Session.default.request(ResourcePath.Orientation.path "2", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: ["x-remote-origin":"ios"]).responseJSON(completionHandler: { (dataResponse) in
            switch (dataResponse.result) {
            case .success(let value) :
                do {
                    let json = JSON(value)
                    if let response = json.dictionary {
                        let result = response["response"]?.string;
                        /* AES cryptor instance */
                        let aes = try AES(key: self.keyForCrypting, blockMode: CBC(iv: self.ivForCrypting.bytes), padding: .pkcs7)
                        let encryptedData = Data(base64Encoded:result!)!
                        let decryptedData = Data(try aes.decrypt(encryptedData.bytes))
                        let decryptedText = String(data: decryptedData, encoding: .utf8)
                        let reply = decryptedText?.replacingOccurrences(of: "\0", with:"");
                        let jsonData = try JSON(reply)
                        observer.onNext(jsonData.rawString()!.convertToDictionary() as AnyObject)
                        observer.onCompleted();
                    }
                }
                catch {
                    print(error);
                    observer.onError(error)
                    return
                }
                break
            case .failure(let error) :
                observer.onError(error)
                break
            }
        })
        return Disposables.create {
            request.cancel()
        }
    })
}

CodePudding user response:

Your code looks strange. The usual sequence is: Original data (for example JSON) encryption base-64 encoding transmission base-64 decoding decryption -> original data (for example JSON).

The encryption will add some padding that should be automatically removed by the decryption as long as you use the same parameters for both. But then you have a base-64 encoding step that is completely strange at that point.

And you are allowed to put all that processing into a separate function.

  • Related