[
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