CredentialAttributes.kt — cleanup and improve docs

This commit is contained in:
Vladimir Krivosheev
2018-11-30 14:38:25 +01:00
parent e55077d97c
commit b5bb97b900
4 changed files with 38 additions and 67 deletions
@@ -230,4 +230,6 @@ fun createKeePassStore(dbFile: Path, masterPasswordFile: Path): PasswordSafe {
keepassDb = store.dbFile.toString()
})
return BasePasswordSafe(settings, store)
}
}
private fun CredentialAttributes.toPasswordStoreable() = if (isPasswordMemoryOnly) CredentialAttributes(serviceName, userName, requestor) else this
@@ -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)
}
@@ -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)
}
@@ -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);
}
/**