db-derby-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Rick Hillegas (JIRA)" <j...@apache.org>
Subject [jira] [Updated] (DERBY-5622) Reduce the chance for hash collisions when checking bootPassword at boot time and when changing password.
Date Tue, 12 Jun 2012 20:00:44 GMT

     [ 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

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


View raw message