//# 5 errors, 335 messages
//#
/*
    //#CipherUtils.java:1:1: class: com.dmdirc.util.CipherUtils
    //#CipherUtils.java:1:1: method: com.dmdirc.util.CipherUtils.com.dmdirc.util.CipherUtils__static_init
 * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.dmdirc.util;

import com.dmdirc.Main;
import com.dmdirc.config.IdentityManager;
import com.dmdirc.logger.ErrorLevel;
import com.dmdirc.logger.Logger;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import net.miginfocom.Base64;

/**
 * Helper class to encrypt and decrypt strings, requests passwords if needed.
 */
public class CipherUtils {
    
    /** Singleton instance. */
    private static CipherUtils me;
    
    /** Encryption cipher. */
    private Cipher ecipher;
    
    /** Decryption cipher. */
    private Cipher dcipher;
    
    /** Salt. */
    private final byte[] SALT = {
        (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
        (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03,
    };
    
    /** Iteration count. */
    private static final int ITERATIONS = 19;
    
    /** Number of auth attemps before failing the attempt. */
    private static final int AUTH_TRIES = 4;
    
    /** User password. */
    private String password;
    
    /**
     * Prevents creation of a new instance of Encipher.
     */
    protected CipherUtils() {
    //#CipherUtils.java:83: method: void com.dmdirc.util.CipherUtils.com.dmdirc.util.CipherUtils()
    //#input(void com.dmdirc.util.CipherUtils()): this
    //#output(void com.dmdirc.util.CipherUtils()): new byte[](CipherUtils#1) num objects
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT.length
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[0]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[1]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[2]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[3]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[4]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[5]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[6]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT[7]
    //#output(void com.dmdirc.util.CipherUtils()): this.SALT
    //#new obj(void com.dmdirc.util.CipherUtils()): new byte[](CipherUtils#1)
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT == &new byte[](CipherUtils#1)
    //#post(void com.dmdirc.util.CipherUtils()): new byte[](CipherUtils#1) num objects == 1
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT.length == 8
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[0] == -87
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[1] == -101
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[2] == -56
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[3] == 50
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[4] == 86
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[5] == 53
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[6] == -29
    //#post(void com.dmdirc.util.CipherUtils()): this.SALT[7] == 3
        // Do nothing
    }
    //#CipherUtils.java:85: end of method: void com.dmdirc.util.CipherUtils.com.dmdirc.util.CipherUtils()
    
    /**
     * Retrieves a singleton instance of CipherUtils.
     * 
     * @return A singleton cipher utils instance.
     */
    public static CipherUtils getCipherUtils() {
        synchronized(CipherUtils.class) {
    //#CipherUtils.java:93: method: CipherUtils com.dmdirc.util.CipherUtils.getCipherUtils()
    //#input(CipherUtils getCipherUtils()): __Class_Obj.__Lock
    //#input(CipherUtils getCipherUtils()): me
    //#output(CipherUtils getCipherUtils()): me
    //#output(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1) num objects
    //#output(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1).SALT
    //#output(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1).__Tag
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1) num objects
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1).length
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[0]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[1]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[2]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[3]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[4]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[5]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[6]
    //#output(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[7]
    //#output(CipherUtils getCipherUtils()): return_value
    //#new obj(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1)
    //#new obj(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)
    //#pre[1] (CipherUtils getCipherUtils()): init'ed(me)
    //#post(CipherUtils getCipherUtils()): me == One-of{old me, &new CipherUtils(getCipherUtils#1)}
    //#post(CipherUtils getCipherUtils()): me != null
    //#post(CipherUtils getCipherUtils()): return_value == One-of{old me, &new CipherUtils(getCipherUtils#1)}
    //#post(CipherUtils getCipherUtils()): return_value != null
    //#post(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1) num objects <= 1
    //#post(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1).SALT == &new byte[](CipherUtils#1)
    //#post(CipherUtils getCipherUtils()): new CipherUtils(getCipherUtils#1).__Tag == com/dmdirc/util/CipherUtils
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1) num objects <= 1
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1).length == 8
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[0] == -87
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[1] == -101
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[2] == -56
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[3] == 50
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[4] == 86
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[5] == 53
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[6] == -29
    //#post(CipherUtils getCipherUtils()): new byte[](CipherUtils#1)[7] == 3
    //#test_vector(CipherUtils getCipherUtils()): me: Inverse{null}, Addr_Set{null}
            if (me == null) {
                me = new CipherUtils();
            }
            
            return me;
        }
    //#CipherUtils.java:99: end of method: CipherUtils com.dmdirc.util.CipherUtils.getCipherUtils()
    }
    
    /**
     * Encrypts a string using the stored settings. Will return null if the
     * automatic user authentication fails - use checkauth and auth.
     * @param str String to encrypt
     * @return Encrypted string
     */
    public String encrypt(final String str) {
        if (!checkAuthed()) {
    //#CipherUtils.java:109: method: String com.dmdirc.util.CipherUtils.encrypt(String)
    //#input(String encrypt(String)): "Unable to decrypt string: "._tainted
    //#input(String encrypt(String)): __Descendant_Table[com/dmdirc/util/CipherUtils]
    //#input(String encrypt(String)): __Descendant_Table[others]
    //#input(String encrypt(String)): __Dispatch_Table.auth()Z
    //#input(String encrypt(String)): __Dispatch_Table.checkAuthed()Z
    //#input(String encrypt(String)): __Dispatch_Table.createCiphers()V
    //#input(String encrypt(String)): __Dispatch_Table.getPassword(Ljava/lang/String;)Ljava/lang/String;
    //#input(String encrypt(String)): __Dispatch_Table.hash(Ljava/lang/String;)Ljava/lang/String;
    //#input(String encrypt(String)): com.dmdirc.logger.ErrorLevel.LOW
    //#input(String encrypt(String)): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[com/dmdirc/ui/interfaces/UIController]
    //#input(String encrypt(String)): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[others]
    //#input(String encrypt(String)): com/dmdirc/ui/interfaces/UIController.__Dispatch_Table.getUserInput(Ljava/lang/String;)Ljava/lang/String;
    //#input(String encrypt(String)): net/miginfocom/Base64.CA
    //#input(String encrypt(String)): str
    //#input(String encrypt(String)): this
    //#input(String encrypt(String)): this.SALT
    //#input(String encrypt(String)): this.__Tag
    //#input(String encrypt(String)): this.dcipher
    //#input(String encrypt(String)): this.ecipher
    //#input(String encrypt(String)): this.password
    //#output(String encrypt(String)): new String(encodeToString#1) num objects
    //#output(String encrypt(String)): return_value
    //#output(String encrypt(String)): this.dcipher
    //#output(String encrypt(String)): this.ecipher
    //#output(String encrypt(String)): this.password
    //#new obj(String encrypt(String)): new String(encodeToString#1)
    //#pre[5] (String encrypt(String)): init'ed(this.dcipher)
    //#pre[11] (String encrypt(String)): this.__Tag == com/dmdirc/util/CipherUtils
    //#pre[6] (String encrypt(String)): (soft) init'ed(this.ecipher)
    //#pre[7] (String encrypt(String)): (soft) init'ed(this.password)
    //#pre[8] (String encrypt(String)): (soft) str != null
    //#presumption(String encrypt(String)): init'ed(com.dmdirc.logger.ErrorLevel.LOW)
    //#presumption(String encrypt(String)): javax.crypto.Cipher:doFinal(...).length@117 in {0, 3..169_538_366}
    //#post(String encrypt(String)): return_value in Addr_Set{null,&new String(encodeToString#1)}
    //#post(String encrypt(String)): init'ed(this.dcipher)
    //#post(String encrypt(String)): init'ed(this.ecipher)
    //#post(String encrypt(String)): init'ed(this.password)
    //#post(String encrypt(String)): new String(encodeToString#1) num objects <= 1
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.lang.Throwable:__curr_excep_obj
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.lang.String:toCharArray
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.spec.PBEKeySpec
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.SecretKeyFactory:getInstance
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.SecretKeyFactory:generateSecret
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.SecretKey:getAlgorithm
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.Cipher:getInstance
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.spec.PBEParameterSpec
    //#unanalyzed(String encrypt(String)): Effects-of-calling:javax.crypto.Cipher:init
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.logger.Logger:userError
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.security.MessageDigest:getInstance
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.lang.String:getBytes
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.security.MessageDigest:digest
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.nio.charset.Charset:forName
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.lang.String
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.Main:getUI
    //#unanalyzed(String encrypt(String)): Effects-of-calling:getUserInput
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.config.IdentityManager:getGlobalConfig
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.config.ConfigManager:hasOptionString
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.config.ConfigManager:getOption
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.lang.String:isEmpty
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.config.IdentityManager:getConfigIdentity
    //#unanalyzed(String encrypt(String)): Effects-of-calling:com.dmdirc.config.Identity:setOption
    //#unanalyzed(String encrypt(String)): Effects-of-calling:java.lang.String:equals
    //#test_vector(String encrypt(String)): !(this.dcipher == null) & !(this.ecipher == null): {1}, {0}
            if (auth()) {
                createCiphers();
    //#CipherUtils.java:111: ?precondition failure
    //#    com/dmdirc/util/CipherUtils.createCiphers: (soft) this.password != null
    //#    severity: SUPPRESSED
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String encrypt(String)
    //#    basic block: bb_3
    //#    assertion: (soft) this.password != null
    //#    callee: void com/dmdirc/util/CipherUtils.createCiphers()
    //#    callee assertion: (soft) this.password != null
    //#    callee file: CipherUtils.java
    //#    callee precondition index: [3]
    //#    callee srcpos: 187
    //#    VN: this.password
    //#    Expected: Inverse{null}
    //#    Bad: Addr_Set{null}
    //#    Attribs:  Ptr  null in Bad  Soft
            } else {
                return null;
            }
        }
        try {
            return Base64.encodeToString(ecipher.doFinal(str.getBytes("UTF8")), false);
    //#CipherUtils.java:117: ?null dereference
    //#    this.ecipher != null
    //#    severity: MEDIUM
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String encrypt(String)
    //#    basic block: bb_6
    //#    assertion: this.ecipher != null
    //#    VN: this.ecipher
    //#    Expected: Inverse{null} or Invalid
    //#    Bad: Addr_Set{null}
    //#    Attribs:  Ptr  null in Bad
        } catch (BadPaddingException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
    //#CipherUtils.java:119: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String encrypt(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        } catch (IllegalBlockSizeException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
    //#CipherUtils.java:121: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String encrypt(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        } catch (UnsupportedEncodingException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
    //#CipherUtils.java:123: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String encrypt(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        }

        return null;
    //#CipherUtils.java:126: end of method: String com.dmdirc.util.CipherUtils.encrypt(String)
    }
    
    /**
     * Encrypts a string using the stored settings. Will return null if the
     * automatic user authentication fails - use checkauth and auth.
     * @param str String to decrypt
     * @return Decrypted string
     */
    public String decrypt(final String str) {
        if (!checkAuthed()) {
    //#CipherUtils.java:136: method: String com.dmdirc.util.CipherUtils.decrypt(String)
    //#input(String decrypt(String)): "Unable to decrypt string: "._tainted
    //#input(String decrypt(String)): __Descendant_Table[com/dmdirc/util/CipherUtils]
    //#input(String decrypt(String)): __Descendant_Table[others]
    //#input(String decrypt(String)): __Dispatch_Table.auth()Z
    //#input(String decrypt(String)): __Dispatch_Table.checkAuthed()Z
    //#input(String decrypt(String)): __Dispatch_Table.createCiphers()V
    //#input(String decrypt(String)): __Dispatch_Table.getPassword(Ljava/lang/String;)Ljava/lang/String;
    //#input(String decrypt(String)): __Dispatch_Table.hash(Ljava/lang/String;)Ljava/lang/String;
    //#input(String decrypt(String)): com.dmdirc.logger.ErrorLevel.LOW
    //#input(String decrypt(String)): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[com/dmdirc/ui/interfaces/UIController]
    //#input(String decrypt(String)): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[others]
    //#input(String decrypt(String)): com/dmdirc/ui/interfaces/UIController.__Dispatch_Table.getUserInput(Ljava/lang/String;)Ljava/lang/String;
    //#input(String decrypt(String)): net.miginfocom.Base64__static_init.new int[](Base64__static_init#1).length
    //#input(String decrypt(String)): net.miginfocom.Base64__static_init.new int[](Base64__static_init#1)[0..65_535]
    //#input(String decrypt(String)): net/miginfocom/Base64.IA
    //#input(String decrypt(String)): str
    //#input(String decrypt(String)): this
    //#input(String decrypt(String)): this.SALT
    //#input(String decrypt(String)): this.__Tag
    //#input(String decrypt(String)): this.dcipher
    //#input(String decrypt(String)): this.ecipher
    //#input(String decrypt(String)): this.password
    //#output(String decrypt(String)): new String(decrypt#1) num objects
    //#output(String decrypt(String)): return_value
    //#output(String decrypt(String)): this.dcipher
    //#output(String decrypt(String)): this.ecipher
    //#output(String decrypt(String)): this.password
    //#new obj(String decrypt(String)): new String(decrypt#1)
    //#pre[6] (String decrypt(String)): init'ed(this.dcipher)
    //#pre[12] (String decrypt(String)): this.__Tag == com/dmdirc/util/CipherUtils
    //#pre[5] (String decrypt(String)): (soft) init'ed(net.miginfocom.Base64__static_init.new int[](Base64__static_init#1)[0..65_535])
    //#pre[7] (String decrypt(String)): (soft) init'ed(this.ecipher)
    //#pre[8] (String decrypt(String)): (soft) init'ed(this.password)
    //#presumption(String decrypt(String)): init'ed(com.dmdirc.logger.ErrorLevel.LOW)
    //#post(String decrypt(String)): return_value in Addr_Set{null,&new String(decrypt#1)}
    //#post(String decrypt(String)): init'ed(this.dcipher)
    //#post(String decrypt(String)): init'ed(this.ecipher)
    //#post(String decrypt(String)): init'ed(this.password)
    //#post(String decrypt(String)): new String(decrypt#1) num objects <= 1
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.Throwable:__curr_excep_obj
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String:toCharArray
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.spec.PBEKeySpec
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.SecretKeyFactory:getInstance
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.SecretKeyFactory:generateSecret
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.SecretKey:getAlgorithm
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.Cipher:getInstance
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.spec.PBEParameterSpec
    //#unanalyzed(String decrypt(String)): Effects-of-calling:javax.crypto.Cipher:init
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.logger.Logger:userError
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.security.MessageDigest:getInstance
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String:getBytes
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.security.MessageDigest:digest
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.nio.charset.Charset:forName
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.Main:getUI
    //#unanalyzed(String decrypt(String)): Effects-of-calling:getUserInput
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.config.IdentityManager:getGlobalConfig
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.config.ConfigManager:hasOptionString
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.config.ConfigManager:getOption
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String:isEmpty
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.config.IdentityManager:getConfigIdentity
    //#unanalyzed(String decrypt(String)): Effects-of-calling:com.dmdirc.config.Identity:setOption
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String:equals
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String:length
    //#unanalyzed(String decrypt(String)): Effects-of-calling:java.lang.String:charAt
    //#test_vector(String decrypt(String)): !(this.dcipher == null) & !(this.ecipher == null): {1}, {0}
            if (auth()) {
                createCiphers();
    //#CipherUtils.java:138: ?precondition failure
    //#    com/dmdirc/util/CipherUtils.createCiphers: (soft) this.password != null
    //#    severity: SUPPRESSED
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String decrypt(String)
    //#    basic block: bb_3
    //#    assertion: (soft) this.password != null
    //#    callee: void com/dmdirc/util/CipherUtils.createCiphers()
    //#    callee assertion: (soft) this.password != null
    //#    callee file: CipherUtils.java
    //#    callee precondition index: [3]
    //#    callee srcpos: 187
    //#    VN: this.password
    //#    Expected: Inverse{null}
    //#    Bad: Addr_Set{null}
    //#    Attribs:  Ptr  null in Bad  Soft
            } else {
                return null;
            }
        }
        try {
            return new String(dcipher.doFinal(Base64.decode(str)));
    //#CipherUtils.java:144: ?null dereference
    //#    this.dcipher != null
    //#    severity: MEDIUM
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String decrypt(String)
    //#    basic block: bb_6
    //#    assertion: this.dcipher != null
    //#    VN: this.dcipher
    //#    Expected: Inverse{null} or Invalid
    //#    Bad: Addr_Set{null}
    //#    Attribs:  Ptr  null in Bad
        } catch (BadPaddingException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
    //#CipherUtils.java:146: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String decrypt(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        } catch (IllegalBlockSizeException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
    //#CipherUtils.java:148: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String decrypt(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        }
        return null;
    //#CipherUtils.java:150: end of method: String com.dmdirc.util.CipherUtils.decrypt(String)
    }
    
    /**
     * Performs a SHA-512 hash.
     * @param data String to hashed
     * @return hashed string
     */
    public String hash(final String data) {
        try {
            return new String(MessageDigest.getInstance("SHA-512")
    //#CipherUtils.java:160: method: String com.dmdirc.util.CipherUtils.hash(String)
    //#input(String hash(String)): com.dmdirc.logger.ErrorLevel.LOW
    //#input(String hash(String)): data
    //#output(String hash(String)): new String(hash#1) num objects
    //#output(String hash(String)): return_value
    //#new obj(String hash(String)): new String(hash#1)
    //#pre[1] (String hash(String)): (soft) data != null
    //#presumption(String hash(String)): init'ed(com.dmdirc.logger.ErrorLevel.LOW)
    //#presumption(String hash(String)): java.security.MessageDigest:getInstance(...)@160 != null
    //#post(String hash(String)): return_value in Addr_Set{null,&new String(hash#1)}
    //#post(String hash(String)): new String(hash#1) num objects <= 1
            .digest(data.getBytes("UTF8")), Charset.forName("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to hash string");
    //#CipherUtils.java:163: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String hash(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        } catch (IOException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to hash string");
    //#CipherUtils.java:165: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String hash(String)
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
        }
        return null;
    //#CipherUtils.java:167: end of method: String com.dmdirc.util.CipherUtils.hash(String)
    }
    
    /**
     * Checks if a user is authed.
     *
     * @return true if authed, false otherwise
     */
    public boolean checkAuthed() {
        if (dcipher != null && ecipher != null) {
    //#CipherUtils.java:176: method: bool com.dmdirc.util.CipherUtils.checkAuthed()
    //#input(bool checkAuthed()): this
    //#input(bool checkAuthed()): this.dcipher
    //#input(bool checkAuthed()): this.ecipher
    //#output(bool checkAuthed()): return_value
    //#pre[6] (bool checkAuthed()): init'ed(this.dcipher)
    //#pre[7] (bool checkAuthed()): (soft) init'ed(this.ecipher)
    //#post(bool checkAuthed()): init'ed(return_value)
    //#test_vector(bool checkAuthed()): this.dcipher: Addr_Set{null}, Inverse{null}
    //#test_vector(bool checkAuthed()): this.ecipher: Addr_Set{null}, Inverse{null}
            return true;
        }
        return false;
    //#CipherUtils.java:179: end of method: bool com.dmdirc.util.CipherUtils.checkAuthed()
    }
    
    /**
     * creates ciphers.
     */
    protected void createCiphers() {
        try {
            final KeySpec keySpec = new PBEKeySpec(
    //#CipherUtils.java:187: method: void com.dmdirc.util.CipherUtils.createCiphers()
    //#input(void createCiphers()): com.dmdirc.logger.ErrorLevel.LOW
    //#input(void createCiphers()): this
    //#input(void createCiphers()): this.SALT
    //#input(void createCiphers()): this.password
    //#output(void createCiphers()): this.dcipher
    //#output(void createCiphers()): this.ecipher
    //#pre[3] (void createCiphers()): (soft) this.password != null
    //#presumption(void createCiphers()): init'ed(com.dmdirc.logger.ErrorLevel.LOW)
    //#presumption(void createCiphers()): javax.crypto.Cipher:getInstance(...)@191 != null
    //#presumption(void createCiphers()): javax.crypto.Cipher:getInstance(...)@192 != null
    //#presumption(void createCiphers()): javax.crypto.SecretKeyFactory:generateSecret(...)@189 != null
    //#presumption(void createCiphers()): javax.crypto.SecretKeyFactory:getInstance(...)@189 != null
    //#post(void createCiphers()): init'ed(this.dcipher)
    //#post(void createCiphers()): init'ed(this.ecipher)
                    password.toCharArray(), SALT, ITERATIONS);
            final SecretKey key = SecretKeyFactory.
                    getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());
            final AlgorithmParameterSpec paramSpec =
                    new PBEParameterSpec(SALT, ITERATIONS);
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
        } catch (InvalidAlgorithmParameterException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
    //#CipherUtils.java:198: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: void createCiphers()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
            ecipher = null;
            dcipher = null;
        } catch (InvalidKeySpecException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
    //#CipherUtils.java:202: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: void createCiphers()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
            ecipher = null;
            dcipher = null;
        } catch (NoSuchPaddingException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
    //#CipherUtils.java:206: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: void createCiphers()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
            ecipher = null;
            dcipher = null;
        } catch (NoSuchAlgorithmException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
    //#CipherUtils.java:210: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: void createCiphers()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
            ecipher = null;
            dcipher = null;
        } catch (InvalidKeyException e) {
            Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
    //#CipherUtils.java:214: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: void createCiphers()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:userError(ErrorLevel, String)
            ecipher = null;
            dcipher = null;
        }
    }
    //#CipherUtils.java:218: end of method: void com.dmdirc.util.CipherUtils.createCiphers()
    
    /**
     * Auths a user and sets the password.
     *
     * @return true if auth was successful, false otherwise.
     */
    public boolean auth() {
        String passwordHash = null;
    //#CipherUtils.java:226: method: bool com.dmdirc.util.CipherUtils.auth()
    //#input(bool auth()): __Descendant_Table[com/dmdirc/util/CipherUtils]
    //#input(bool auth()): __Descendant_Table[others]
    //#input(bool auth()): __Dispatch_Table.getPassword(Ljava/lang/String;)Ljava/lang/String;
    //#input(bool auth()): __Dispatch_Table.hash(Ljava/lang/String;)Ljava/lang/String;
    //#input(bool auth()): com.dmdirc.logger.ErrorLevel.LOW
    //#input(bool auth()): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[com/dmdirc/ui/interfaces/UIController]
    //#input(bool auth()): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[others]
    //#input(bool auth()): com/dmdirc/ui/interfaces/UIController.__Dispatch_Table.getUserInput(Ljava/lang/String;)Ljava/lang/String;
    //#input(bool auth()): this
    //#input(bool auth()): this.__Tag
    //#input(bool auth()): this.password
    //#output(bool auth()): return_value
    //#output(bool auth()): this.password
    //#pre[1] (bool auth()): (soft) init'ed(this.password)
    //#pre[3] (bool auth()): (soft) this.__Tag == com/dmdirc/util/CipherUtils
    //#presumption(bool auth()): com.dmdirc.config.IdentityManager:getConfigIdentity(...)@242 != null
    //#presumption(bool auth()): com.dmdirc.config.IdentityManager:getGlobalConfig(...)@229 != null
    //#presumption(bool auth()): com.dmdirc.config.IdentityManager:getGlobalConfig(...)@230 != null
    //#presumption(bool auth()): com.dmdirc.config.IdentityManager:getGlobalConfig(...)@232 != null
    //#presumption(bool auth()): com.dmdirc.config.IdentityManager:getGlobalConfig(...)@234 != null
    //#presumption(bool auth()): getUserInput(...)@266 != null
    //#post(bool auth()): init'ed(return_value)
    //#post(bool auth()): init'ed(this.password)
    //#unanalyzed(bool auth()): Effects-of-calling:java.lang.Throwable:__curr_excep_obj
    //#unanalyzed(bool auth()): Effects-of-calling:com.dmdirc.logger.Logger:userError
    //#unanalyzed(bool auth()): Effects-of-calling:java.security.MessageDigest:getInstance
    //#unanalyzed(bool auth()): Effects-of-calling:java.lang.String:getBytes
    //#unanalyzed(bool auth()): Effects-of-calling:java.security.MessageDigest:digest
    //#unanalyzed(bool auth()): Effects-of-calling:java.nio.charset.Charset:forName
    //#unanalyzed(bool auth()): Effects-of-calling:java.lang.String
    //#unanalyzed(bool auth()): Effects-of-calling:com.dmdirc.Main:getUI
    //#unanalyzed(bool auth()): Effects-of-calling:getUserInput
    //#test_vector(bool auth()): com.dmdirc.config.ConfigManager:hasOptionString(...)@229: {0}, {1}
    //#test_vector(bool auth()): com.dmdirc.config.ConfigManager:hasOptionString(...)@232: {0}, {1}
    //#test_vector(bool auth()): java.lang.String:equals(...)@245: {1}, {0}
    //#test_vector(bool auth()): java.lang.String:isEmpty(...)@238: {0}, {1}
        String prompt = "Please enter your password";
        int tries = 1;
        if (IdentityManager.getGlobalConfig().hasOptionString("encryption", "password")) {
    //#CipherUtils.java:229: Warning: method not available - call not analyzed
    //#    call on ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#CipherUtils.java:229: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
            password = IdentityManager.getGlobalConfig().getOption("encryption", "password");
    //#CipherUtils.java:230: Warning: method not available - call not analyzed
    //#    call on ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#CipherUtils.java:230: Warning: method not available - call not analyzed
    //#    call on String com.dmdirc.config.ConfigManager:getOption(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: String com.dmdirc.config.ConfigManager:getOption(String, String)
        } else {
            if (IdentityManager.getGlobalConfig().hasOptionString("encryption",
    //#CipherUtils.java:232: Warning: method not available - call not analyzed
    //#    call on ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#CipherUtils.java:232: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
                    "passwordHash")) {
                passwordHash = IdentityManager.getGlobalConfig().getOption("encryption",
    //#CipherUtils.java:234: Warning: method not available - call not analyzed
    //#    call on ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: ConfigManager com.dmdirc.config.IdentityManager:getGlobalConfig()
    //#CipherUtils.java:234: Warning: method not available - call not analyzed
    //#    call on String com.dmdirc.config.ConfigManager:getOption(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: String com.dmdirc.config.ConfigManager:getOption(String, String)
                        "passwordHash");
            }

            while ((password == null || password.isEmpty()) && tries < AUTH_TRIES) {
                password = getPassword(prompt);
                if (passwordHash == null) {
                    passwordHash = hash(password);
                    IdentityManager.getConfigIdentity().setOption("encryption", 
    //#CipherUtils.java:242: Warning: method not available - call not analyzed
    //#    call on Identity com.dmdirc.config.IdentityManager:getConfigIdentity()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: Identity com.dmdirc.config.IdentityManager:getConfigIdentity()
    //#CipherUtils.java:242: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.config.Identity:setOption(String, String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    unanalyzed callee: void com.dmdirc.config.Identity:setOption(String, String, String)
                            "passwordHash", passwordHash);
                }
                if (!hash(password).equals(passwordHash)) {
    //#CipherUtils.java:245: ?null dereference
    //#    hash(...) != null
    //#    severity: MEDIUM
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: bool auth()
    //#    basic block: bb_10
    //#    assertion: hash(...) != null
    //#    VN: hash(...)
    //#    Expected: Inverse{null} or Invalid
    //#    Bad: Addr_Set{null}
    //#    Attribs:  Ptr  null in Bad
                    prompt = "<html>Password mis-match<br>Please re-enter "
                            + "your password</html>";
                    tries++;
                    password = null;
                }
            }
        }
        if (tries == AUTH_TRIES) {
            return false;
        }
        return true;
    //#CipherUtils.java:256: end of method: bool com.dmdirc.util.CipherUtils.auth()
    }
    
    /**
     * Requests the encryption password from the user.
     * 
     * @param prompt The prompt to show
     * @return The user-specified password
     */
    protected String getPassword(final String prompt) {
        return Main.getUI().getUserInput(prompt);
    //#CipherUtils.java:266: method: String com.dmdirc.util.CipherUtils.getPassword(String)
    //#CipherUtils.java:266: Warning: method not available - call not analyzed
    //#    call on UIController com.dmdirc.Main:getUI()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String getPassword(String)
    //#    unanalyzed callee: UIController com.dmdirc.Main:getUI()
    //#CipherUtils.java:266: Warning: method not available - call not analyzed
    //#    call on String getUserInput(String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.util.CipherUtils
    //#    method: String getPassword(String)
    //#    unanalyzed callee: String getUserInput(String)
    //#input(String getPassword(String)): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[com/dmdirc/ui/interfaces/UIController]
    //#input(String getPassword(String)): com/dmdirc/ui/interfaces/UIController.__Descendant_Table[others]
    //#input(String getPassword(String)): com/dmdirc/ui/interfaces/UIController.__Dispatch_Table.getUserInput(Ljava/lang/String;)Ljava/lang/String;
    //#input(String getPassword(String)): prompt
    //#output(String getPassword(String)): return_value
    //#presumption(String getPassword(String)): com.dmdirc.Main:getUI(...).__Tag@266 == com/dmdirc/ui/interfaces/UIController
    //#presumption(String getPassword(String)): com.dmdirc.Main:getUI(...)@266 != null
    //#post(String getPassword(String)): init'ed(return_value)
    //#CipherUtils.java:266: end of method: String com.dmdirc.util.CipherUtils.getPassword(String)
    }
}
    //#output(com.dmdirc.util.CipherUtils__static_init): __Descendant_Table[com/dmdirc/util/CipherUtils]
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.auth()Z
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.checkAuthed()Z
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.createCiphers()V
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.decrypt(Ljava/lang/String;)Ljava/lang/String;
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.encrypt(Ljava/lang/String;)Ljava/lang/String;
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.getPassword(Ljava/lang/String;)Ljava/lang/String;
    //#output(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.hash(Ljava/lang/String;)Ljava/lang/String;
    //#post(com.dmdirc.util.CipherUtils__static_init): __Descendant_Table[com/dmdirc/util/CipherUtils] == &__Dispatch_Table
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.auth()Z == &auth
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.checkAuthed()Z == &checkAuthed
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.createCiphers()V == &createCiphers
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.decrypt(Ljava/lang/String;)Ljava/lang/String; == &decrypt
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.encrypt(Ljava/lang/String;)Ljava/lang/String; == &encrypt
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.getPassword(Ljava/lang/String;)Ljava/lang/String; == &getPassword
    //#post(com.dmdirc.util.CipherUtils__static_init): __Dispatch_Table.hash(Ljava/lang/String;)Ljava/lang/String; == &hash
    //#CipherUtils.java:: end of method: com.dmdirc.util.CipherUtils.com.dmdirc.util.CipherUtils__static_init
    //#CipherUtils.java:: end of class: com.dmdirc.util.CipherUtils
