fix #1 UniversalPrivateKey & UniversalPublicKey
This commit is contained in:
parent
194fe22afa
commit
640ceb448e
1
.gitignore
vendored
1
.gitignore
vendored
@ -41,4 +41,5 @@ out/
|
|||||||
# Other
|
# Other
|
||||||
.kotlin
|
.kotlin
|
||||||
.idea
|
.idea
|
||||||
|
.gigaide
|
||||||
/kotlin-js-store/yarn.lock
|
/kotlin-js-store/yarn.lock
|
||||||
|
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.5.9-SNAPSHOT"
|
version = "0.6.1-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -20,6 +20,8 @@ import net.sergeych.crypto2.Container.Companion.createWith
|
|||||||
* - [addRecipients] and various [plus] operators to add recipients
|
* - [addRecipients] and various [plus] operators to add recipients
|
||||||
* - [updateData] to change decrypted content for the same recipient keys
|
* - [updateData] to change decrypted content for the same recipient keys
|
||||||
*
|
*
|
||||||
|
* Note that container _is serialized encrypted_.
|
||||||
|
*
|
||||||
* Some rules:
|
* Some rules:
|
||||||
*
|
*
|
||||||
* When adding public key recipient, it is faster to use your known [SecretKey], but you
|
* When adding public key recipient, it is faster to use your known [SecretKey], but you
|
||||||
@ -126,8 +128,9 @@ sealed class Container {
|
|||||||
abstract fun updateData(newPlainData: UByteArray, randomFill: IntRange? = null): Container
|
abstract fun updateData(newPlainData: UByteArray, randomFill: IntRange? = null): Container
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binary encoded version. It is desirable to include [Container] as an object, though,
|
* Binary encoded _encrypted_ version. It is desirable to include [Container] as an object, though,
|
||||||
* especially when using custom serialization (Json, Boss, etc), it is serializable.
|
* especially when using custom serialization (Json, Boss, etc.), it is serializable. Note that
|
||||||
|
* serialized data is always encrypted.
|
||||||
* Still, if you need it in binary form, this is a shortcut. You can use [decode] or call
|
* Still, if you need it in binary form, this is a shortcut. You can use [decode] or call
|
||||||
* [BipackDecoder.decode] to deserialize the binary form.
|
* [BipackDecoder.decode] to deserialize the binary form.
|
||||||
*/
|
*/
|
||||||
@ -474,6 +477,12 @@ sealed class Container {
|
|||||||
inline fun <reified T> decrypt(cipherData: UByteArray, vararg keys: DecryptingKey): T? =
|
inline fun <reified T> decrypt(cipherData: UByteArray, vararg keys: DecryptingKey): T? =
|
||||||
decryptAsUBytes(cipherData, *keys)?.let { BipackDecoder.decode<T>(it.asByteArray()) }
|
decryptAsUBytes(cipherData, *keys)?.let { BipackDecoder.decode<T>(it.asByteArray()) }
|
||||||
|
|
||||||
|
inline fun <reified T> decrypt(cipherData: UByteArray, ring: UniversalRing): T? =
|
||||||
|
decode(cipherData)
|
||||||
|
.decryptWith(ring)?.let {
|
||||||
|
BipackDecoder.decode<T>(it.asByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
fun decryptAsUBytes(cipherData: UByteArray, vararg keys: DecryptingKey): UByteArray? =
|
fun decryptAsUBytes(cipherData: UByteArray, vararg keys: DecryptingKey): UByteArray? =
|
||||||
decode(cipherData).decryptWith(*keys)
|
decode(cipherData).decryptWith(*keys)
|
||||||
|
|
||||||
|
@ -6,6 +6,11 @@ enum class KeysmagicNumber(val label: String) {
|
|||||||
defaultSymmetric( "sym"),
|
defaultSymmetric( "sym"),
|
||||||
defaultSession( "ssn"),
|
defaultSession( "ssn"),
|
||||||
defaultVerifying( "ver"),
|
defaultVerifying( "ver"),
|
||||||
|
|
||||||
|
defaultSigningSecret( "sig"),
|
||||||
|
|
||||||
|
defaultUniversalPublic( "pub+"),
|
||||||
|
defaultUniversalPrivate( "prv+"),
|
||||||
;
|
;
|
||||||
|
|
||||||
}
|
}
|
@ -23,6 +23,8 @@ class SigningSecretKey(
|
|||||||
VerifyingPublicKey(Signature.ed25519SkToPk(keyBytes)).also { cachedPublicKey = it }
|
VerifyingPublicKey(Signature.ed25519SkToPk(keyBytes)).also { cachedPublicKey = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val magic: KeysmagicNumber = KeysmagicNumber.defaultSigningSecret
|
||||||
|
|
||||||
override fun sign(message: UByteArray): UByteArray = Signature.detached(message, keyBytes)
|
override fun sign(message: UByteArray): UByteArray = Signature.detached(message, keyBytes)
|
||||||
|
|
||||||
override fun seal(message: UByteArray, expiresAt: Instant?): Seal =
|
override fun seal(message: UByteArray, expiresAt: Instant?): Seal =
|
||||||
|
@ -8,7 +8,6 @@ sealed class UniversalKey: KeyInstance {
|
|||||||
|
|
||||||
abstract val keyBytes: UByteArray
|
abstract val keyBytes: UByteArray
|
||||||
|
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
open val magic: KeysmagicNumber = KeysmagicNumber.Unknown
|
open val magic: KeysmagicNumber = KeysmagicNumber.Unknown
|
||||||
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package net.sergeych.crypto2
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combination of private/secret keys suitable for both decryption and signing.
|
||||||
|
*
|
||||||
|
* It contains two cryptographically independent keys to raise security to a maximum.
|
||||||
|
* Any converted keys poses a threat while technically possible so we avoid it.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("uprv")
|
||||||
|
class UniversalPrivateKey(
|
||||||
|
val signingKey: SigningSecretKey,
|
||||||
|
val decryptingKey: SecretKey
|
||||||
|
) : UniversalKey(), DecryptingKey by decryptingKey, SigningKey by signingKey {
|
||||||
|
|
||||||
|
override val keyBytes by lazy { signingKey.keyBytes + decryptingKey.keyBytes }
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
override val magic = KeysmagicNumber.defaultUniversalPrivate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Important! Private key combines signing and decrypting keys, but uses
|
||||||
|
* it of the decrypting one to be used in keyring.
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
|
override val id: KeyId = decryptingKey.id
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Corresponding public key able to verify amd encrypt data created by this
|
||||||
|
* private key.
|
||||||
|
*/
|
||||||
|
val publicKey by lazy {
|
||||||
|
UniversalPublicKey(signingKey.verifyingKey, decryptingKey.publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Generate 2 new random keys (4 key pairs under the hood) to securely signd and
|
||||||
|
* decrypt data.
|
||||||
|
*/
|
||||||
|
fun new() = UniversalPrivateKey(SigningSecretKey.new(), SecretKey.new())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package net.sergeych.crypto2
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combination of public keys suitable for both encryption and verification. A counterpart
|
||||||
|
* of the [UniversalPrivateKey], available also as [UniversalPrivateKey.publicKey].
|
||||||
|
*
|
||||||
|
* When using [UniversalRing] and [Container], data encrypted with instances og this class
|
||||||
|
* can be decrypted with rings containing the corresponding [UniversalPrivateKey].
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("upub")
|
||||||
|
class UniversalPublicKey(
|
||||||
|
val verifyingKey: VerifyingPublicKey,
|
||||||
|
val encryptingKey: PublicKey
|
||||||
|
): UniversalKey(), VerifyingKey by verifyingKey, EncryptingKey by encryptingKey{
|
||||||
|
|
||||||
|
override val keyBytes by lazy { verifyingKey.keyBytes + encryptingKey.keyBytes }
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
override val magic = KeysmagicNumber.defaultUniversalPublic
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Important! Private key combines signing and decrypting keys, but uses
|
||||||
|
* it of the decrypting one to be used in keyring.
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
|
override val id: KeyId = encryptingKey.id
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -360,4 +360,34 @@ class KeysTest {
|
|||||||
assertTrue { mk.check(k4.verifyingKey) }
|
assertTrue { mk.check(k4.verifyingKey) }
|
||||||
assertTrue { mk.check(k5.verifyingKey) }
|
assertTrue { mk.check(k5.verifyingKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCombinedKeys() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val k1 = UniversalPrivateKey.new()
|
||||||
|
val k2 = UniversalPrivateKey.new()
|
||||||
|
val k3: UniversalPrivateKey = unpack(pack(k1))
|
||||||
|
assertEquals(k1, k3)
|
||||||
|
assertEquals(k1.publicKey, k3.publicKey)
|
||||||
|
assertEquals(k1.signingKey, k3.signingKey)
|
||||||
|
assertEquals(k1.verifyingKey, k3.verifyingKey)
|
||||||
|
|
||||||
|
val k4: UniversalPublicKey = unpack(pack(k1.publicKey))
|
||||||
|
assertEquals(k1.publicKey, k4)
|
||||||
|
assertEquals(k1.publicKey.encryptingKey, k4.encryptingKey)
|
||||||
|
assertEquals(k1.publicKey.verifyingKey, k4.verifyingKey)
|
||||||
|
|
||||||
|
val data =
|
||||||
|
"""We hold these truths to be self-evident, that all men are created equal,
|
||||||
|
|that they are endowed by their Creator with certain unalienable Rights,
|
||||||
|
|that among these are Life, Liberty and the pursuit of Happiness."""
|
||||||
|
.trimMargin()
|
||||||
|
|
||||||
|
val kr1 = UniversalRing.from(k1)
|
||||||
|
val kr2: UniversalRing = UniversalRing.from(k2)
|
||||||
|
|
||||||
|
val bytes = Container.encrypt(data, k2.publicKey)
|
||||||
|
assertNull(Container.decrypt<String>(bytes, kr1))
|
||||||
|
assertEquals(data, Container.decrypt<String>(bytes, kr2))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user