IDEA-160375 Exception on attempt to connect to db with passwords converted from idea 2016.2

This commit is contained in:
Vladimir Krivosheev
2016-08-26 18:21:01 +02:00
parent 34e66036c3
commit dc6f6778b2
4 changed files with 24 additions and 20 deletions
@@ -84,7 +84,7 @@ fun findGenericPassword(serviceName: ByteArray, accountName: String?): Credentia
checkForError("find", LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes?.size ?: 0, accountNameBytes, passwordSize, passwordRef, itemRef))
val pointer = passwordRef.value ?: return null
val password = SecureString(pointer.getByteArray(0, passwordSize.get(0)))
val password = OneTimeString(pointer.getByteArray(0, passwordSize.get(0)))
LIBRARY.SecKeychainItemFreeContent(null, pointer)
var effectiveAccountName = accountName
@@ -59,7 +59,7 @@ fun Credentials?.isEmpty() = this == null || (userName == null && password == nu
// input will be cleared
@JvmOverloads
fun SecureString(value: ByteArray, offset: Int = 0, length: Int = value.size - offset): OneTimeString {
fun OneTimeString(value: ByteArray, offset: Int = 0, length: Int = value.size - offset): OneTimeString {
if (length == 0) {
return OneTimeString(ArrayUtil.EMPTY_CHAR_ARRAY)
}
@@ -82,6 +82,8 @@ fun SecureString(value: ByteArray, offset: Int = 0, length: Int = value.size - o
return OneTimeString(charArray, 0, charBuffer.position())
}
private val oneTimeStringEnabled = com.intellij.util.SystemProperties.getBooleanProperty("one.time.string.enabled", false)
@Suppress("EqualsOrHashCode")
// todo - eliminate toString
class OneTimeString @JvmOverloads constructor(value: CharArray, offset: Int = 0, length: Int = value.size) : CharArrayCharSequence(value, offset, offset + length) {
@@ -90,17 +92,22 @@ class OneTimeString @JvmOverloads constructor(value: CharArray, offset: Int = 0,
constructor(value: String): this(value.toCharArray()) {
}
private fun consume() {
if (!consumed.compareAndSet(null, ExceptionUtil.currentStackTrace())) {
private fun consume(willBeCleared: Boolean) {
if (!oneTimeStringEnabled) {
return
}
if (!willBeCleared) {
consumed.get()?.let { throw IllegalStateException("Already consumed at $it") }
}
else if (!consumed.compareAndSet(null, ExceptionUtil.currentStackTrace())) {
throw IllegalStateException("Already consumed at ${consumed.get()}")
}
}
@JvmOverloads
fun toString(clear: Boolean = true): String {
if (clear) {
consume()
}
consume(clear)
// todo clear
return super.toString()
}
@@ -108,12 +115,10 @@ class OneTimeString @JvmOverloads constructor(value: CharArray, offset: Int = 0,
// string will be cleared and not valid after
@JvmOverloads
fun toByteArray(clear: Boolean = true): ByteArray {
if (clear) {
consume()
}
consume(clear)
val result = Charsets.UTF_8.encode(CharBuffer.wrap(myChars, myStart, length))
if (clear) {
if (clear && oneTimeStringEnabled) {
myChars.fill('\u0000', myStart, myEnd)
}
return result.toByteArray()
@@ -121,9 +126,7 @@ class OneTimeString @JvmOverloads constructor(value: CharArray, offset: Int = 0,
@JvmOverloads
fun toCharArray(clear: Boolean = true): CharArray {
if (clear) {
consume()
}
consume(clear)
// todo clear
return chars
}
@@ -136,7 +139,7 @@ class OneTimeString @JvmOverloads constructor(value: CharArray, offset: Int = 0,
}
fun appendTo(builder: StringBuilder) {
consumed.get()?.let { throw IllegalStateException("Already consumed at $it") }
consume(false)
builder.append(myChars, myStart, length)
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.util;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,15 +52,15 @@ public class PasswordUtil {
@NotNull
public static char[] decodePasswordAsCharArray(@Nullable String password) throws NumberFormatException {
if (password == null) {
if (StringUtil.isEmpty(password)) {
return ArrayUtil.EMPTY_CHAR_ARRAY;
}
char[] result = new char[password.length()];
for (int i = 0; i < password.length(); i += 4) {
char[] result = new char[password.length() / 4];
for (int i = 0, j = 0; i < password.length(); i += 4, j++) {
int c = Integer.parseInt(password.substring(i, i + 4), 16);
c ^= 0xdfaa;
result[i] = new Character((char)c).charValue();
result[j] = new Character((char)c).charValue();
}
return result;
}
@@ -200,7 +200,7 @@ public class EncryptionUtil {
if (len < 0 || len > plain.length - 4) {
throw new IllegalStateException("Unmatched password is used");
}
return CredentialAttributesKt.SecureString(plain, 4, len);
return CredentialAttributesKt.OneTimeString(plain, 4, len);
}
/**