[ 
https://issues.apache.org/jira/browse/DERBY-5622?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Rick Hillegas updated DERBY-5622:
---------------------------------

    Attachment: derby-5622-repro.sql
                derby-5622-TT-fixWithTestScaffolding.diff
                derby-5622-01-aa-decryptEncryptedSample.diff

Attaching derby-5622-01-aa-decryptEncryptedSample.diff, 
derby-5622-TT-fixWithTestScaffolding.diff, and derby-5622-repro.sql. The first 
file is a patch for this issue, described below. The second file is the patch 
plus some extra edits which disable the pre-existing checks for whether the 
user has supplied the correct old bootpassword. The third file is a script 
demonstrating that a new check, added by the patch, runs if you make it past 
the pre-existing checks. To demonstrate the new behavior, apply 
derby-5622-TT-fixWithTestScaffolding.diff, run derby-5622-repro.sql, and look 
at the stack trace in derby.log to confirm that the new code was exercised.

Regression tests passed cleanly for me after applying 
derby-5622-01-aa-decryptEncryptedSample.diff.

Here is a re-cap of the current behavior in 10.8. Thanks to Dag and Knut for 
investigating this behavior here and on DERBY-2687:

1) Let us say that the database was encrypted and booted using a bootpassword, 
which we will call ${actualBootPassword}.

2) Note that when the database was created, Derby generated an encryption key, 
scrambled it using ${actualBootPassword}, and stored the scrambled result in 
service.properties. The generated encryption key is what is actually used to 
encrypt and decrypt data in the conglomerates and log files. In particular, the 
storage layer uses the encryption key to construct an object which encrypts 
data in the conglomerates. Let us call this object the storeEncrypter.

3) The DBO can change the boot password by calling 
SYSCS_UTIL.SYSCS_SET_DATABASE_PASSWORD. If the key argument is "bootPassword", 
then Derby expects that the value argument will be a string of the form 
"${oldBootPassword,${newBootPassword}", where ${oldBootPassword} is expected to 
be ${actualBootPassword}. Note the comma in the value argument string. The 
comma is important. It separates ${oldBootPassword} from ${newBootPassword}, 
effectively allowing the caller to pass 3 arguments to a procedure which 
expects two arguments. This peculiar syntax is discussed earlier on this issue 
and on DERBY-4328. Here is an example of how to call 
SYSCS_SET_DATABASE_PASSWORD to change the bootpassword:

call syscs_util.syscs_set_database_property( 'bootPassword', 
'bpass_02,bpass_03' );

4) Before honoring this command, Derby needs to verify that the caller knows 
what they are doing. In particular, Derby needs to verify that 
${oldBootPassword} really is ${actualBootPassword}. Presumably for security 
reasons, Derby doesn't stash ${actualBootPassword} anywhere, either on disk or 
in memory. Derby tries to verify the equivalence of ${oldBootPassword} and 
${actualBootPassword} by first using ${oldBootPassword} to unscramble the 
encryption key stored in service.properties. Then Derby runs the following 
checks to verify that the unscrambed key is the actual encryption key used by 
storeEncrypter:

i) Derby verifies that a 16 bit digest (appended to the scrambled result) is 
the same as a 16 bit digest computed from the unscrambled result. We believe 
that Derby more or less randomly generated the original encryption key. 
Therefore, the chance that a wrong ${oldBootPassword} will pass this check is 
smallish, viz., 1 in 2<sup>16</sup>.

ii) If the previous check succeeds, then Derby performs a second check. Derby 
verifies that the unscrambled encryption key gives rise to the same 
initialization vector used by the storeEncrypter. An initialization vector is a 
seed for the encryption logic, as described here: 
http://en.wikipedia.org/wiki/Initialization_vector. I don't understand the 
combinatorics of constructing the initialization vector. So I can't estimate 
the risk that this check will succeed on a wrong ${oldBootPassword} after 
passing the (i) check. However, the initialization vector is at most 128 bits 
long, so the probability of a collision is at least 1 in 2<sup>128</sup>.

So the chance that a bad ${oldBootPassword} will pass both (i) and (ii) is at 
least 1 in 2<sup>144</sup>.

So far I have described the process of changing the bootpassword in the current 
Derby release, 10.8. The attached derby-5622-01-aa-decryptEncryptedSample.diff 
patch adds a third check in case (i) and (ii) both succeed:

(iii) We construct a candidateDecrypter from the unscrambled encryption key. 
Then we construct a stereotypical block of cleartext, which is 8192 bits long. 
We encrypt the cleartext using the storeEncrypter. Then we decrypt the result 
using the candidateDecrypter. If the final result matches the original 
cleartext, then the check passes. Again, I don't understand the combinatorics 
of Derby's encryption algorithms on this stereotypical cleartext, and I don't 
understand how they interact on keys which pass the (i) and (ii) checks. 
However, the probability that a bad ${oldBootPassword} will pass this new check 
is at least 1 in 2<sup>8192</sup>.

So the chance that a bad ${oldBootPassword} will pass (i), (ii), and (iii) is 
at least 1 in 2<sup>8336</sup>. That, however, is a very big number.

I think that this additional check probably reduces the risk of this bug to an 
acceptable level, particularly if we advise people to backup their database 
before changing the bootpassword.

I welcome your review as well as any suggestions about how to test this new 
codepath: I'm not sure how to coax Derby past (i) and (ii) without 
instrumenting the code in a way which decreases security. But here is an idea:

We could introduce a special ${oldBootPassword}, viz., "derby-5622-bypass". 
When Derby sees that value, it skips (i) and (ii) and falls into check (iii). 
If somehow we still pass check (iii), then we raise an exception.

Thanks in advance for your advice.

                
> Reduce the chance for hash collisions when checking bootPassword at boot time 
> and when changing password.
> ---------------------------------------------------------------------------------------------------------
>
>                 Key: DERBY-5622
>                 URL: https://issues.apache.org/jira/browse/DERBY-5622
>             Project: Derby
>          Issue Type: Improvement
>          Components: Store
>            Reporter: Dag H. Wanvik
>         Attachments: derby-5622-01-aa-decryptEncryptedSample.diff, 
> derby-5622-TT-fixWithTestScaffolding.diff, derby-5622-instrumentation.diff, 
> derby-5622-repro.sql, repro.sh
>
>
> There are two issues, already seen in DERBY-2687:
>    "the boot issue": there is a 1/2**16 chance that a wrong bootPassword will 
> allow boot to proceed (but since its decoded key is wrong the boot will fail).
>    "the password change" issue: similarly, there is a chance that the wrong 
> bootPassword will be accepted trying to change it via 
>     SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('bootPassword', ...) at least for 
> algorithms that do not check IV (initialization vector) in addition to the
>     digest, e.g. "DES/ECB/NoPadding"
> The latter case may lead to data corruption, cf. DERBY-2687 discussion. I 
> think the risk is fairly low, though: One would need to have execution 
> permission to change the property if SQL authorization is used, and in most 
> scenarios the supplied existing password would be correct. But since the 
> results can be bad, it would be good to reduce or eliminate the risk.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: 
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to