diff --git a/platform/credential-store/src/PasswordSafeImpl.kt b/platform/credential-store/src/PasswordSafeImpl.kt index 4f80f9c8dcd9..fcc0c1b8db69 100644 --- a/platform/credential-store/src/PasswordSafeImpl.kt +++ b/platform/credential-store/src/PasswordSafeImpl.kt @@ -230,4 +230,6 @@ fun createKeePassStore(dbFile: Path, masterPasswordFile: Path): PasswordSafe { keepassDb = store.dbFile.toString() }) return BasePasswordSafe(settings, store) -} \ No newline at end of file +} + +private fun CredentialAttributes.toPasswordStoreable() = if (isPasswordMemoryOnly) CredentialAttributes(serviceName, userName, requestor) else this \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt b/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt index f99ed9742ab8..59fcda0ecbec 100644 --- a/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt +++ b/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt @@ -2,24 +2,20 @@ package com.intellij.credentialStore import com.intellij.ide.passwordSafe.PasswordSafe -import com.intellij.util.ArrayUtil import com.intellij.util.text.nullize import org.jetbrains.annotations.Contract -import java.nio.ByteBuffer -import java.nio.CharBuffer -import java.nio.charset.CodingErrorAction const val SERVICE_NAME_PREFIX = "IntelliJ Platform" fun generateServiceName(subsystem: String, key: String) = "$SERVICE_NAME_PREFIX $subsystem — $key" /** - * requestor is deprecated. Never use it in new code. + * Consider using [generateServiceName] to generate [serviceName]. + * + * [requestor] is deprecated (never use it in a new code). */ data class CredentialAttributes @JvmOverloads constructor(val serviceName: String, val userName: String? = null, val requestor: Class<*>? = null, val isPasswordMemoryOnly: Boolean = false) -fun CredentialAttributes.toPasswordStoreable() = if (isPasswordMemoryOnly) CredentialAttributes(serviceName, userName, requestor) else this - // user cannot be empty, but password can be class Credentials(user: String?, val password: OneTimeString? = null) { constructor(user: String?, password: String?) : this(user, password?.let(::OneTimeString)) @@ -28,7 +24,7 @@ class Credentials(user: String?, val password: OneTimeString? = null) { constructor(user: String?, password: ByteArray?) : this(user, password?.let { OneTimeString(password) }) - val userName: String? = user.nullize() + val userName = user.nullize() fun getPasswordAsString() = password?.toString() @@ -44,7 +40,7 @@ class Credentials(user: String?, val password: OneTimeString? = null) { @Suppress("FunctionName", "DeprecatedCallableAddReplaceWith") /** - * DEPRECATED. Never use it in a new code. + * @deprecated Never use it in a new code. */ @Deprecated("Never use it in a new code.") fun CredentialAttributes(requestor: Class<*>, userName: String?) = CredentialAttributes(requestor.name, userName, requestor) @@ -71,29 +67,4 @@ fun getAndMigrateCredentials(oldAttributes: CredentialAttributes, newAttributes: } } return credentials -} - -@Suppress("FunctionName") -@JvmOverloads -fun OneTimeString(value: ByteArray, offset: Int = 0, length: Int = value.size - offset, clearable: Boolean = false): OneTimeString { - if (length == 0) { - return OneTimeString(ArrayUtil.EMPTY_CHAR_ARRAY) - } - - // jdk decodes to heap array, but since this code is very critical, we cannot rely on it, so, we don't use Charsets.UTF_8.decode() - val charsetDecoder = Charsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE) - val charArray = CharArray((value.size * charsetDecoder.maxCharsPerByte().toDouble()).toInt()) - charsetDecoder.reset() - val charBuffer = CharBuffer.wrap(charArray) - var cr = charsetDecoder.decode(ByteBuffer.wrap(value, offset, length), charBuffer, true) - if (!cr.isUnderflow) { - cr.throwException() - } - cr = charsetDecoder.flush(charBuffer) - if (!cr.isUnderflow) { - cr.throwException() - } - - value.fill(0, offset, offset + length) - return OneTimeString(charArray, 0, charBuffer.position(), clearable = clearable) } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/credentialStore/OneTimeString.kt b/platform/platform-api/src/com/intellij/credentialStore/OneTimeString.kt index 10d4182a680b..c2f817bcce09 100644 --- a/platform/platform-api/src/com/intellij/credentialStore/OneTimeString.kt +++ b/platform/platform-api/src/com/intellij/credentialStore/OneTimeString.kt @@ -2,10 +2,13 @@ package com.intellij.credentialStore import com.intellij.openapi.util.text.StringUtil +import com.intellij.util.ArrayUtil import com.intellij.util.ExceptionUtil import com.intellij.util.io.toByteArray import com.intellij.util.text.CharArrayCharSequence +import java.nio.ByteBuffer import java.nio.CharBuffer +import java.nio.charset.CodingErrorAction import java.util.concurrent.atomic.AtomicReference /** @@ -89,4 +92,29 @@ class OneTimeString @JvmOverloads constructor(value: CharArray, offset: Int = 0, consume(false) builder.append(myChars, myStart, length) } +} + +@Suppress("FunctionName") +@JvmOverloads +fun OneTimeString(value: ByteArray, offset: Int = 0, length: Int = value.size - offset, clearable: Boolean = false): OneTimeString { + if (length == 0) { + return OneTimeString(ArrayUtil.EMPTY_CHAR_ARRAY) + } + + // jdk decodes to heap array, but since this code is very critical, we cannot rely on it, so, we don't use Charsets.UTF_8.decode() + val charsetDecoder = Charsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE) + val charArray = CharArray((value.size * charsetDecoder.maxCharsPerByte().toDouble()).toInt()) + charsetDecoder.reset() + val charBuffer = CharBuffer.wrap(charArray) + var cr = charsetDecoder.decode(ByteBuffer.wrap(value, offset, length), charBuffer, true) + if (!cr.isUnderflow) { + cr.throwException() + } + cr = charsetDecoder.flush(charBuffer) + if (!cr.isUnderflow) { + cr.throwException() + } + + value.fill(0, offset, offset + length) + return OneTimeString(charArray, 0, charBuffer.position(), clearable = clearable) } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java index 51412bf1e67e..2b1819ac6dc3 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java @@ -2,8 +2,8 @@ package com.intellij.ide.passwordSafe.impl.providers; import com.intellij.credentialStore.CredentialAttributes; -import com.intellij.credentialStore.CredentialAttributesKt; import com.intellij.credentialStore.OneTimeString; +import com.intellij.credentialStore.OneTimeStringKt; import com.intellij.openapi.vfs.CharsetToolkit; import org.jetbrains.annotations.NotNull; @@ -81,17 +81,6 @@ public class EncryptionUtil { return key; } - /** - * Generate key based on password - * - * @param password the password to use - * @return the generated key - */ - public static byte[] genPasswordKey(@NotNull String password) { - return genKey(hash(getUTF8Bytes(password))); - } - - /** * Encrypt key (does not use salting, so the encryption result is the same for the same input) * @@ -111,25 +100,6 @@ public class EncryptionUtil { } } - /** - * Decrypt key (does not use salting, so the encryption result is the same for the same input) - * - * @param password the secret key to use - * @param encryptedKey the key to decrypt - * @return the decrypted key - */ - public static byte[] decryptKey(byte[] password, byte[] encryptedKey) { - try { - Cipher c = Cipher.getInstance(ENCRYPT_KEY_ALGORITHM); - c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(password, SECRET_KEY_ALGORITHM), CBC_SALT_KEY); - return c.doFinal(encryptedKey); - } - catch (Exception e) { - throw new IllegalStateException(ENCRYPT_KEY_ALGORITHM + " is not available", e); - } - } - - /** * Encrypt key (does not use salting, so the encryption result is the same for the same input) * @@ -179,7 +149,7 @@ public class EncryptionUtil { if (len < 0 || len > plain.length - 4) { throw new IllegalStateException("Unmatched password is used"); } - return CredentialAttributesKt.OneTimeString(plain, 4, len); + return OneTimeStringKt.OneTimeString(plain, 4, len); } /**