Fix zero-padding and handling of empty passwords (meaning protection on, but no password to remove it) for XSSF workbook protection.

https://bz.apache.org/bugzilla/show_bug.cgi?id=59920

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1754744 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David North 2016-08-01 12:51:24 +00:00
parent b8f1906f2a
commit 984683ab4b
3 changed files with 41 additions and 14 deletions

View File

@ -374,20 +374,22 @@ public class CryptoFunctions {
// SET Verifier TO 0x0000 // SET Verifier TO 0x0000
short verifier = 0; short verifier = 0;
// FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER if (!"".equals(password)) {
for (int i = arrByteChars.length-1; i >= 0; i--) { // FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
// SET Verifier TO Intermediate3 BITWISE XOR PasswordByte for (int i = arrByteChars.length-1; i >= 0; i--) {
// SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars[i];
}
// as we haven't prepended the password length into the input array
// we need to do it now separately ...
verifier = rotateLeftBase15Bit(verifier); verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars[i]; verifier ^= arrByteChars.length;
// RETURN Verifier BITWISE XOR 0xCE4B
verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
} }
// as we haven't prepended the password length into the input array
// we need to do it now separately ...
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars.length;
// RETURN Verifier BITWISE XOR 0xCE4B
verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
return verifier & 0xFFFF; return verifier & 0xFFFF;
} }

View File

@ -55,8 +55,8 @@ public class XSSFPaswordHelper {
cur.toFirstContentToken(); cur.toFirstContentToken();
if (hashAlgo == null) { if (hashAlgo == null) {
int hash = CryptoFunctions.createXorVerifier1(password); int hash = CryptoFunctions.createXorVerifier1(password);
cur.insertAttributeWithValue(getAttrName(prefix, "password"), cur.insertAttributeWithValue(getAttrName(prefix, "password"),
Integer.toHexString(hash).toUpperCase(Locale.ROOT)); String.format(Locale.ROOT, "%04X", hash).toUpperCase(Locale.ROOT));
} else { } else {
SecureRandom random = new SecureRandom(); SecureRandom random = new SecureRandom();
byte salt[] = random.generateSeed(16); byte salt[] = random.generateSeed(16);

View File

@ -80,6 +80,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCalcMode; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCalcMode;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STUnsignedShortHex;
public final class TestXSSFSheet extends BaseTestXSheet { public final class TestXSSFSheet extends BaseTestXSheet {
@ -1099,6 +1100,30 @@ public final class TestXSSFSheet extends BaseTestXSheet {
wb.close(); wb.close();
} }
@Test
public void protectSheet_emptyPassword() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet();
CTSheetProtection pr = sheet.getCTWorksheet().getSheetProtection();
assertNull("CTSheetProtection should be null by default", pr);
String password = "";
sheet.protectSheet(password);
pr = sheet.getCTWorksheet().getSheetProtection();
assertNotNull("CTSheetProtection should be not null", pr);
assertTrue("sheet protection should be on", pr.isSetSheet());
assertTrue("object protection should be on", pr.isSetObjects());
assertTrue("scenario protection should be on", pr.isSetScenarios());
int hashVal = CryptoFunctions.createXorVerifier1(password);
STUnsignedShortHex xpassword = pr.xgetPassword();
int actualVal = Integer.parseInt(xpassword.getStringValue(),16);
assertEquals("well known value for top secret hash should match", hashVal, actualVal);
sheet.protectSheet(null);
assertNull("protectSheet(null) should unset CTSheetProtection", sheet.getCTWorksheet().getSheetProtection());
wb.close();
}
@Test @Test
public void protectSheet_lowlevel_2013() throws IOException { public void protectSheet_lowlevel_2013() throws IOException {
String password = "test"; String password = "test";