View | Details | Raw Unified | Return to bug 59135
Collapse All | Expand All

(-)src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java (-45 / +58 lines)
Lines 338-343 Link Here
338
    };
338
    };
339
339
340
    /**
340
    /**
341
     * Create the verifier for xor obfuscation (method 1)
342
     *
343
     * @see <a href="http://msdn.microsoft.com/en-us/library/dd926947.aspx">2.3.7.1 Binary Document Password Verifier Derivation Method 1</a>
344
     * @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
345
     * 
346
     * @param password the password
347
     * @return the verifier (actually a short value)
348
     */
349
    public static int createXorVerifier1(String password) {
350
        byte[] arrByteChars = getRevisionsPassword(password);
351
        
352
        // SET Verifier TO 0x0000
353
        short verifier = 0;
354
355
        // FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
356
        for (int i = arrByteChars.length-1; i >= 0; i--) {
357
            // SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
358
            verifier = rotateLeftBase15Bit(verifier);
359
            verifier ^= arrByteChars[i];
360
        }
361
362
        // as we haven't prepended the password length into the input array
363
        // we need to do it now separately ...
364
        verifier = rotateLeftBase15Bit(verifier);
365
        verifier ^= arrByteChars.length;
366
        
367
        // RETURN Verifier BITWISE XOR 0xCE4B
368
        verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
369
        
370
        return verifier;
371
    }
372
 
373
    /**
341
     * This method generates the xor verifier for word documents &lt; 2007 (method 2).
374
     * This method generates the xor verifier for word documents &lt; 2007 (method 2).
342
     * Its output will be used as password input for the newer word generations which
375
     * Its output will be used as password input for the newer word generations which
343
     * utilize a real hashing algorithm like sha1.
376
     * utilize a real hashing algorithm like sha1.
Lines 360-377 Link Here
360
            // Truncate the password to 15 characters
393
            // Truncate the password to 15 characters
361
            password = password.substring(0, Math.min(password.length(), maxPasswordLength));
394
            password = password.substring(0, Math.min(password.length(), maxPasswordLength));
362
395
363
            // Construct a new NULL-terminated string consisting of single-byte characters:
396
            byte[] arrByteChars = getRevisionsPassword(password);
364
            //  -- > Get the single-byte values by iterating through the Unicode characters of the truncated Password.
365
            //   --> For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
366
            byte[] arrByteChars = new byte[password.length()];
367
            
397
            
368
            for (int i = 0; i < password.length(); i++) {
369
                int intTemp = password.charAt(i);
370
                byte lowByte = (byte)(intTemp & 0x00FF);
371
                byte highByte = (byte)((intTemp & 0xFF00) >> 8);
372
                arrByteChars[i] = (lowByte != 0 ? lowByte : highByte);
373
            }
374
375
            // Compute the high-order word of the new key:
398
            // Compute the high-order word of the new key:
376
399
377
            // --> Initialize from the initial code array (see below), depending on the passwords length. 
400
            // --> Initialize from the initial code array (see below), depending on the passwords length. 
Lines 391-419 Link Here
391
            }
414
            }
392
            
415
            
393
            // Compute the low-order word of the new key:
416
            // Compute the low-order word of the new key:
394
            
417
            int verifier = createXorVerifier1(password);
395
            // SET Verifier TO 0x0000
396
            short verifier = 0;
397
398
            // FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
399
            for (int i = arrByteChars.length-1; i >= 0; i--) {
400
                // SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
401
                verifier = rotateLeftBase15Bit(verifier);
402
                verifier ^= arrByteChars[i];
403
            }
404
405
            // as we haven't prepended the password length into the input array
406
            // we need to do it now separately ...
407
            verifier = rotateLeftBase15Bit(verifier);
408
            verifier ^= arrByteChars.length;
409
            
410
            // RETURN Verifier BITWISE XOR 0xCE4B
411
            verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
412
418
413
            // The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64],
419
            // The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64],
414
            // and that value shall be hashed as defined by the attribute values.
420
            // and that value shall be hashed as defined by the attribute values.
415
            
421
            
416
            LittleEndian.putShort(generatedKey, 0, verifier);
422
            LittleEndian.putShort(generatedKey, 0, (short)verifier);
417
            LittleEndian.putShort(generatedKey, 2, (short)highOrderWord);
423
            LittleEndian.putShort(generatedKey, 2, (short)highOrderWord);
418
        }
424
        }
419
        
425
        
Lines 444-464 Link Here
444
    }
450
    }
445
451
446
    /**
452
    /**
447
     * Create the verifier for xor obfuscation (method 1)
448
     *
449
     * @see <a href="http://msdn.microsoft.com/en-us/library/dd926947.aspx">2.3.7.1 Binary Document Password Verifier Derivation Method 1</a>
450
     * @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
451
     * 
452
     * @param password the password
453
     * @return the verifier (actually a short value)
454
     */
455
    public static int createXorVerifier1(String password) {
456
        // the verifier for method 1 is part of the verifier for method 2
457
        // so we simply chop it from there
458
        return createXorVerifier2(password) & 0xFFFF;
459
    }
460
 
461
    /**
462
     * Create the xor key for xor obfuscation, which is used to create the xor array (method 1)
453
     * Create the xor key for xor obfuscation, which is used to create the xor array (method 1)
463
     *
454
     *
464
     * @see <a href="http://msdn.microsoft.com/en-us/library/dd924704.aspx">2.3.7.2 Binary Document XOR Array Initialization Method 1</a>
455
     * @see <a href="http://msdn.microsoft.com/en-us/library/dd924704.aspx">2.3.7.2 Binary Document XOR Array Initialization Method 1</a>
Lines 530-533 Link Here
530
        short intermediate3 = (short)(intermediate1 | intermediate2);
521
        short intermediate3 = (short)(intermediate1 | intermediate2);
531
        return intermediate3;
522
        return intermediate3;
532
    }
523
    }
524
    
525
    /**
526
     * The provided Unicode password string is converted to a ANSI string
527
     *
528
     * @param utfString
529
     * @return
530
     */
531
    private static byte[] getRevisionsPassword(String password) {
532
        // Construct a new NULL-terminated string consisting of single-byte characters:
533
        //  -- > Get the single-byte values by iterating through the Unicode characters of the truncated Password.
534
        //   --> For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
535
        byte[] arrByteChars = new byte[password.length()];
536
        
537
        for (int i = 0; i < password.length(); i++) {
538
            int intTemp = password.charAt(i);
539
            byte lowByte = (byte)(intTemp & 0x00FF);
540
            byte highByte = (byte)((intTemp & 0xFF00) >> 8);
541
            arrByteChars[i] = (lowByte != 0 ? lowByte : highByte);
542
        }
543
544
        return arrByteChars;
545
    }
533
}
546
}
(-)src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java (+21 lines)
Lines 26-31 Link Here
26
import static org.junit.Assert.assertTrue;
26
import static org.junit.Assert.assertTrue;
27
import static org.junit.Assert.fail;
27
import static org.junit.Assert.fail;
28
28
29
import java.io.FileInputStream;
29
import java.io.IOException;
30
import java.io.IOException;
30
import java.util.List;
31
import java.util.List;
31
32
Lines 56-61 Link Here
56
import org.apache.poi.hssf.record.WindowTwoRecord;
57
import org.apache.poi.hssf.record.WindowTwoRecord;
57
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
58
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
58
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
59
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
60
import org.apache.poi.poifs.crypt.CryptoFunctions;
59
import org.apache.poi.ss.SpreadsheetVersion;
61
import org.apache.poi.ss.SpreadsheetVersion;
60
import org.apache.poi.ss.formula.ptg.Area3DPtg;
62
import org.apache.poi.ss.formula.ptg.Area3DPtg;
61
import org.apache.poi.ss.formula.ptg.Ptg;
63
import org.apache.poi.ss.formula.ptg.Ptg;
Lines 1222-1225 Link Here
1222
        
1224
        
1223
        wb.close();
1225
        wb.close();
1224
    }
1226
    }
1227
1228
    @Test
1229
    public void bug59135() throws IOException {
1230
        HSSFWorkbook wb1 = new HSSFWorkbook();
1231
        wb1.createSheet().protectSheet("1111.2222.3333.1234");
1232
        HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb1);
1233
        wb1.close();
1234
        
1235
        assertEquals((short)0xb86b, wb2.getSheetAt(0).getPassword());
1236
        wb2.close();
1237
1238
        HSSFWorkbook wb3 = new HSSFWorkbook();
1239
        wb3.createSheet().protectSheet("1111.2222.3333.12345");
1240
        HSSFWorkbook wb4 = HSSFTestDataSamples.writeOutAndReadBack(wb3);
1241
        wb3.close();
1242
        
1243
        assertEquals((short)0xbecc, wb4.getSheetAt(0).getPassword());
1244
        wb4.close();
1245
    }
1225
}
1246
}

Return to bug 59135