Skip to content

Commit

Permalink
Merge branch 'feat/encrypt_padding' into 'master'
Browse files Browse the repository at this point in the history
Feat/encrypt padding

See merge request TankerHQ/sdk-android!264
  • Loading branch information
JMounier committed Jul 7, 2022
2 parents 20febe7 + ef8fa59 commit 900b7fa
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,53 @@ class EncryptionSessionTests : TankerSpec() {
val e = shouldThrow<TankerFutureException> { tankerBob.decrypt(private).get() }
assertThat((e.cause is TankerException)).isEqualTo(true)
}

private val encryptionSessionOverhead = 57
private val encryptionSessionPaddedOverhead = encryptionSessionOverhead + 1

@Test
fun can_encrypt_with_auto_padding_by_default() {
val plaintext = "my clear data is clear!"
val lengthWithPadme = 24
val sess = tankerAlice.createEncryptionSession(null).get()
val encrypted = sess.encrypt(plaintext.toByteArray()).get()

assertThat(encrypted.size - encryptionSessionPaddedOverhead).isEqualTo(lengthWithPadme)
assertThat(String(tankerAlice.decrypt(encrypted).get())).isEqualTo(plaintext)
}

@Test
fun can_encrypt_with_auto_padding() {
val plaintext = "my clear data is clear!"
val lengthWithPadme = 24
val encryptOptions = EncryptionOptions().paddingStep(Padding.auto)
val sess = tankerAlice.createEncryptionSession(encryptOptions).get()
val encrypted = sess.encrypt(plaintext.toByteArray()).get()

assertThat(encrypted.size - encryptionSessionPaddedOverhead).isEqualTo(lengthWithPadme)
assertThat(String(tankerAlice.decrypt(encrypted).get())).isEqualTo(plaintext)
}

@Test
fun can_encrypt_with_no_padding() {
val plaintext = "L'assommoir"
val encryptOptions = EncryptionOptions().paddingStep(Padding.off)
val sess = tankerAlice.createEncryptionSession(encryptOptions).get()
val encrypted = sess.encrypt(plaintext.toByteArray()).get()

assertThat(encrypted.size - encryptionSessionOverhead).isEqualTo(plaintext.length)
assertThat(String(tankerAlice.decrypt(encrypted).get())).isEqualTo(plaintext)
}

@Test
fun can_encrypt_with_a_padding_step() {
val plaintext = "Au Bonheur des Dames"
val step = 13
val encryptOptions = EncryptionOptions().paddingStep(Padding.step(step))
val sess = tankerAlice.createEncryptionSession(encryptOptions).get()
val encrypted = sess.encrypt(plaintext.toByteArray()).get()

assertThat((encrypted.size - encryptionSessionPaddedOverhead) % step).isEqualTo(0)
assertThat(String(tankerAlice.decrypt(encrypted).get())).isEqualTo(plaintext)
}
}
104 changes: 104 additions & 0 deletions tanker-bindings/src/androidTest/kotlin/io/tanker/api/Tanker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.junit.Before
import org.junit.Test
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import kotlin.IllegalArgumentException

class TankerTests : TankerSpec() {
@Before
Expand Down Expand Up @@ -134,6 +135,89 @@ class TankerTests : TankerSpec() {
tanker.stop().get()
}

private val simpleEncryptionOverhead = 17
private val simplePaddedEncryptionOverhead = simpleEncryptionOverhead + 1

@Test
fun auto_padding_by_default() {
val tanker = Tanker(options)
val identity = tc.createIdentity()
tanker.start(identity).get()
tanker.registerIdentity(PassphraseVerification("pass")).get()

val plaintext = "my clear data is clear!"
val lengthWithPadme = 24
val encrypted = tanker.encrypt(plaintext.toByteArray()).get()
assertThat(encrypted.size - simplePaddedEncryptionOverhead).isEqualTo(lengthWithPadme)

val decrypted = tanker.decrypt(encrypted).get()
assertThat(String(decrypted)).isEqualTo(plaintext)

tanker.stop().get()
}

@Test
fun can_set_padding_auto() {
val tanker = Tanker(options)
val identity = tc.createIdentity()
tanker.start(identity).get()
tanker.registerIdentity(PassphraseVerification("pass")).get()

val plaintext = "my clear data is clear!"
val lengthWithPadme = 24
val encryptOptions = EncryptionOptions().paddingStep(Padding.auto)
val encrypted = tanker.encrypt(plaintext.toByteArray(), encryptOptions).get()
assertThat(encrypted.size - simplePaddedEncryptionOverhead).isEqualTo(lengthWithPadme)

val decrypted = tanker.decrypt(encrypted).get()
assertThat(String(decrypted)).isEqualTo(plaintext)

tanker.stop().get()
}

@Test
fun can_disable_padding() {
val tanker = Tanker(options)
val identity = tc.createIdentity()
tanker.start(identity).get()
tanker.registerIdentity(PassphraseVerification("pass")).get()

val plaintext = "plain text"
val encryptOptions = EncryptionOptions().paddingStep(Padding.off)
val encrypted = tanker.encrypt(plaintext.toByteArray(), encryptOptions).get()
assertThat(encrypted.size - simpleEncryptionOverhead).isEqualTo(plaintext.length)

val decrypted = tanker.decrypt(encrypted).get()
assertThat(String(decrypted)).isEqualTo(plaintext)

tanker.stop().get()
}

@Test
fun can_set_the_padding_step() {
val tanker = Tanker(options)
val identity = tc.createIdentity()
tanker.start(identity).get()
tanker.registerIdentity(PassphraseVerification("pass")).get()

val plaintext = "plain text"
val encryptOptions = EncryptionOptions().paddingStep(Padding.step(13))
val encrypted = tanker.encrypt(plaintext.toByteArray(), encryptOptions).get()
assertThat((encrypted.size - simplePaddedEncryptionOverhead) % 13).isEqualTo(0)

val decrypted = tanker.decrypt(encrypted).get()
assertThat(String(decrypted)).isEqualTo(plaintext)

tanker.stop().get()
}

@Test
fun cannot_set_a_bad_padding_step() {
listOf(-1, 0, 1).forEach {
shouldThrow<IllegalArgumentException> { Padding.step(it) }
}
}

@Test
fun can_stop_tanker_while_a_call_is_in_flight() {
val tanker = Tanker(options)
Expand Down Expand Up @@ -165,6 +249,26 @@ class TankerTests : TankerSpec() {
tanker.stop().get()
}

@Test
fun can_stream_encrypt_with_padding() {
val tanker = Tanker(options)
val identity = tc.createIdentity()
tanker.start(identity).get()
tanker.registerIdentity(PassphraseVerification("pass")).get()

val plaintext = ByteArray(3 * 1024 * 1024 + 2)
val clear = InputStreamWrapper(plaintext.inputStream())

val encryptor = tanker.encrypt(clear).get()
val encrypted = TankerInputStream(encryptor).readBytes()
assertThat(encrypted).hasSize(3211512)
val decryptor = tanker.decrypt(InputStreamWrapper(encrypted.inputStream())).get()

val decrypted = TankerInputStream(decryptor).readBytes()
assertThat(decrypted).isEqualTo(plaintext)
tanker.stop().get()
}

@Test
fun can_encrypt_share_and_decrypt_between_two_users() {
val aliceId = tc.createIdentity()
Expand Down
14 changes: 12 additions & 2 deletions tanker-bindings/src/main/kotlin/io/tanker/api/EncryptionOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import com.sun.jna.Structure
*/
open class EncryptionOptions : Structure() {
// NOTE: Remember to keep the version in sync w/ the c++!
@JvmField var version: Byte = 3
@JvmField var version: Byte = 4
@JvmField var shareWithUsers = Pointer(0)
@JvmField var nbUsers = 0
@JvmField var shareWithGroups = Pointer(0)
@JvmField var nbGroups = 0
@JvmField var shareWithSelf = 1.toByte()
@JvmField var paddingStep = Padding.auto.native_value

/**
* JNA does not support having a StringArray directly in a struct,
Expand Down Expand Up @@ -58,7 +59,16 @@ open class EncryptionOptions : Structure() {
return this
}

/**
* Sets the padding step.
* @param paddingStep A Padding object
*/
fun paddingStep(paddingStep: Padding): EncryptionOptions {
this.paddingStep = paddingStep.native_value
return this
}

override fun getFieldOrder(): List<String> {
return listOf("version", "shareWithUsers", "nbUsers", "shareWithGroups", "nbGroups", "shareWithSelf")
return listOf("version", "shareWithUsers", "nbUsers", "shareWithGroups", "nbGroups", "shareWithSelf", "paddingStep")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class EncryptionSession(private val csess: Pointer) {
val inBuf = Memory(data.size.toLong().coerceAtLeast(1))
inBuf.write(0, data, 0, data.size)

val encryptedSize = lib.tanker_encryption_session_encrypted_size(data.size.toLong())
val encryptedSize = lib.tanker_encryption_session_encrypted_size(csess, data.size.toLong())
val outBuf = Memory(encryptedSize)

val futurePtr = lib.tanker_encryption_session_encrypt(csess, outBuf, inBuf, data.size.toLong())
Expand Down
38 changes: 38 additions & 0 deletions tanker-bindings/src/main/kotlin/io/tanker/api/Padding.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.tanker.api

/**
* Padding control for data encryption.
*/
sealed class Padding(internal val native_value: Int) {
/**
* Allowed values for Padding variables
*/
companion object {
/**
* This is the default option.
*/
@JvmStatic val auto : Padding = Auto

/**
* Disables padding.
*/
@JvmStatic val off : Padding = Off

/**
* Pads the data up to a multiple of value before encryption.
* To disable padding, use Off().
* @param value A >= 2 integer.
*/
@JvmStatic fun step(value: Int): Padding = Step(value)
}

private object Auto : Padding(0)
private object Off : Padding(1)

private class Step(value: Int) : Padding(value) {
init {
if (value <= 1)
throw IllegalArgumentException("Invalid padding step, the value must be >= 2.")
}
}
}
4 changes: 3 additions & 1 deletion tanker-bindings/src/main/kotlin/io/tanker/api/Tanker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.sun.jna.*
import io.tanker.bindings.*
import io.tanker.datastore.*
import io.tanker.jni.KVMx86Bug
import kotlin.IllegalArgumentException

/**
* Main entry point for the Tanker SDK. Can open a TankerSession.
Expand Down Expand Up @@ -335,7 +336,8 @@ class Tanker(tankerOptions: TankerOptions) {
val inBuf = Memory(data.size.toLong().coerceAtLeast(1))
inBuf.write(0, data, 0, data.size)

val encryptedSize = lib.tanker_encrypted_size(data.size.toLong())
val paddingStep = options?.paddingStep ?: Padding.auto.native_value
val encryptedSize = lib.tanker_encrypted_size(data.size.toLong(), paddingStep)
val outBuf = Memory(encryptedSize)

val futurePtr = lib.tanker_encrypt(tanker, outBuf, inBuf, data.size.toLong(), options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ interface TankerLib : AsyncLib, DatastoreLib, Library {

fun tanker_set_log_handler(handler: LogHandlerCallback): Void

fun tanker_encrypted_size(clear_size: Long): Long
fun tanker_encrypted_size(clear_size: Long, padding_step: Int): Long
fun tanker_decrypted_size(encrypted_data: Pointer, encrypted_size: Long): ExpectedPointer
fun tanker_get_resource_id(encrypted_data: Pointer, encrypted_size: Long): ExpectedPointer

Expand All @@ -128,7 +128,7 @@ interface TankerLib : AsyncLib, DatastoreLib, Library {

fun tanker_encryption_session_open(session: SessionPointer, options: EncryptionOptions): FuturePointer
fun tanker_encryption_session_close(encSess: EncryptionSessionPointer): FuturePointer
fun tanker_encryption_session_encrypted_size(clear_size: Long): Long
fun tanker_encryption_session_encrypted_size(encSess: EncryptionSessionPointer, clear_size: Long): Long
fun tanker_encryption_session_get_resource_id(encSess: EncryptionSessionPointer): ExpectedPointer
fun tanker_encryption_session_encrypt(encSess: EncryptionSessionPointer, encrypted_data: Pointer,
data: Pointer, data_size: Long): FuturePointer
Expand Down

0 comments on commit 900b7fa

Please sign in to comment.