protected function computeOValue($userPassword, $ownerPassword = '')
    {
        $revision = $this->revision;
        // Algorithm 3: Computing the encryption dictionary’s O (owner password) value

        // a) Pad or truncate the owner password string as described in step (a) of
        //    "Algorithm 2: Computing an encryption key". If there is no owner password,
        //    use the user password instead.
        if ('' === $ownerPassword)
            $ownerPassword = $userPassword;

        $s = substr($ownerPassword . $this->padding, 0, 32);

        // b) Initialize the MD5 hash function and pass the result of step (a) as input to
        //    this function.
        $s = md5($s, true);

        // c) (Security handlers of revision 3 or greater) Do the following 50 times:
        //    Take the output from the previous MD5 hash and pass it as input into a new MD5 hash.
        if ($revision >= 3) {
            for ($i = 0; $i < 50; $i++)
                $s = md5($s, true);
        }

        // d) Create an RC4 encryption key using the first n bytes of the output from the
        //    final MD5 hash, where n shall always be 5 for security handlers of revision 2
        //    but, for security handlers of revision 3 or greater, shall depend on the value
        //    of the encryption dictionary’s Length entry.
        $encryptionKey = substr($s, 0, $this->keyLength);

        // e) Pad or truncate the user password string as described in step (a) of
        //    "Algorithm 2: Computing an encryption key".
        $s = substr($userPassword . $this->padding, 0, 32);

        // f) Encrypt the result of step (e), using an RC4 encryption function with
        //    the encryption key obtained in step (d).
        $s = $this->arcfour($encryptionKey, $s);

        // g) (Security handlers of revision 3 or greater) Do the following 19 times:
        //    Take the output from the previous invocation of the RC4 function and pass
        //    it as input to a new invocation of the function; use an encryption key
        //    generated by taking each byte of the encryption key obtained in step (d)
        //    and performing an XOR (exclusive or) operation between that byte and the
        //    single-byte value of the iteration counter (from 1 to 19).
        if ($revision >= 3) {
            for ($i = 1; $i <= 19; $i++) {
                $tmp = array();
                for ($j = 0; $j < $this->keyLength; $j++) {
                    $tmp[$j] = ord($encryptionKey[$j]) ^ $i;
                    $tmp[$j] = chr($tmp[$j]);
                }
                $s = $this->arcfour(join('', $tmp), $s);
            }
        }

        // h) Store the output from the final invocation of the RC4 function as the value
        //    of the O entry in the encryption dictionary.
        return $s;
    }