forked from sergeych/crypto2
		
	missing initial files + publishing
This commit is contained in:
		
							parent
							
								
									aaa8c436b0
								
							
						
					
					
						commit
						f429cfe418
					
				@ -87,3 +87,21 @@ kotlin {
 | 
				
			|||||||
        val nativeTest by getting
 | 
					        val nativeTest by getting
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					publishing {
 | 
				
			||||||
 | 
					    val mavenToken by lazy {
 | 
				
			||||||
 | 
					        File("${System.getProperty("user.home")}/.gitea_token").readText()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    repositories {
 | 
				
			||||||
 | 
					        maven {
 | 
				
			||||||
 | 
					            credentials(HttpHeaderCredentials::class) {
 | 
				
			||||||
 | 
					                name = "Authorization"
 | 
				
			||||||
 | 
					                value = mavenToken
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            url = uri("https://gitea.sergeych.net/api/packages/SergeychWorks/maven")
 | 
				
			||||||
 | 
					            authentication {
 | 
				
			||||||
 | 
					                create("Authorization", HttpHeaderAuthentication::class)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/InitCrypto.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/InitCrypto.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.LibsodiumInitializer
 | 
				
			||||||
 | 
					import kotlinx.coroutines.sync.Mutex
 | 
				
			||||||
 | 
					import kotlinx.coroutines.sync.withLock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private var isReady = false
 | 
				
			||||||
 | 
					private val readyAccess = Mutex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Library initialization: should be called before all other calls.
 | 
				
			||||||
 | 
					 * It is safe and with little performance penalty to call it multiple times.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					suspend fun initCrypto() {
 | 
				
			||||||
 | 
					    // faster to check with no lock
 | 
				
			||||||
 | 
					    if( !isReady) {
 | 
				
			||||||
 | 
					        readyAccess.withLock {
 | 
				
			||||||
 | 
					            // recheck with lock, it could be ready by now
 | 
				
			||||||
 | 
					            if( !isReady ) {
 | 
				
			||||||
 | 
					                LibsodiumInitializer.initialize()
 | 
				
			||||||
 | 
					                isReady = true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/Seal.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/Seal.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Serializable
 | 
				
			||||||
 | 
					class Seal(
 | 
				
			||||||
 | 
					    val publicKey: SigningKey.Public,
 | 
				
			||||||
 | 
					    val signature: UByteArray
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    inline fun verify(message: UByteArray) = publicKey.verify(signature, message)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										65
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/SignedBox.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/SignedBox.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 | 
					import kotlinx.serialization.Transient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Multi-signed data box. Use [SignedBox.invoke] to easily create
 | 
				
			||||||
 | 
					 * instances and [SignedBox.plus] to add more signatures (signing keys), and
 | 
				
			||||||
 | 
					 * [SignedBox.contains] to check for a specific key signature presence.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * It is serializable and checks integrity on deserialization. If any of seals does not
 | 
				
			||||||
 | 
					 * match the signed [message], it throws [IllegalSignatureException] _on deserialization_.
 | 
				
			||||||
 | 
					 * E.g., if you have it deserialized, it is ok, check it contains all needed keys among
 | 
				
			||||||
 | 
					 * signers.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * __The main constructor is used for deserializing only__. Don't use it directly unless you
 | 
				
			||||||
 | 
					 * know what you are doing as it may be dangerous.Use one of the above to create or change it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Serializable
 | 
				
			||||||
 | 
					class SignedBox(
 | 
				
			||||||
 | 
					    val message: UByteArray,
 | 
				
			||||||
 | 
					    private val seals: List<Seal>,
 | 
				
			||||||
 | 
					    @Transient
 | 
				
			||||||
 | 
					    private val checkOnInit: Boolean = true
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * If this instance is not signed by a given key, return new instance signed also by this
 | 
				
			||||||
 | 
					     * key, or return unchanged (same) object if it is already signed by this key; you
 | 
				
			||||||
 | 
					     * _can't assume it always returns a copied object!_
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    operator fun plus(key: SigningKey.Secret): SignedBox =
 | 
				
			||||||
 | 
					        if (key.publicKey in this) this
 | 
				
			||||||
 | 
					        else SignedBox(message, seals + key.seal(message), false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check that it is signed with a specified key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    operator fun contains(publicKey: SigningKey.Public): Boolean {
 | 
				
			||||||
 | 
					        return seals.any { it.publicKey == publicKey }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init {
 | 
				
			||||||
 | 
					        if (seals.isEmpty()) throw IllegalArgumentException("there should be at least one seal")
 | 
				
			||||||
 | 
					        if (checkOnInit) {
 | 
				
			||||||
 | 
					            if (!seals.all { it.verify(message) }) throw IllegalSignatureException()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Create a new instance with a specific data sealed by one or more
 | 
				
			||||||
 | 
					         * keys. At least one key is required to disallow providing not-signed
 | 
				
			||||||
 | 
					         * instances, e.g. [SignedBox] is guaranteed to be properly sealed when
 | 
				
			||||||
 | 
					         * successfully instantiated.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param data a message to sign
 | 
				
			||||||
 | 
					         * @param keys a list of keys to sign with, should be at least one key.
 | 
				
			||||||
 | 
					         * @throws IllegalArgumentException if keys are not specified.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        operator fun invoke(data: UByteArray, vararg keys: SigningKey.Secret): SignedBox =
 | 
				
			||||||
 | 
					            SignedBox(data, keys.map { it.seal(data) }, false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										77
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/SigningKey.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/SigningKey.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.signature.InvalidSignatureException
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.signature.Signature
 | 
				
			||||||
 | 
					import kotlinx.serialization.SerialName
 | 
				
			||||||
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 | 
					import net.sergeych.crypto2.SigningKey.Secret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Keys in general: public, secret and later symmetric too.
 | 
				
			||||||
 | 
					 * Keys could be compared to each other for equality and used
 | 
				
			||||||
 | 
					 * as a Map keys (not sure about js).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Use [Secret.pair] to create new keys.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Serializable
 | 
				
			||||||
 | 
					sealed class SigningKey {
 | 
				
			||||||
 | 
					    abstract val packed: UByteArray
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun equals(other: Any?): Boolean {
 | 
				
			||||||
 | 
					        return other is SigningKey && other.packed contentEquals packed
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun hashCode(): Int {
 | 
				
			||||||
 | 
					        return packed.contentHashCode()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun toString(): String = packed.encodeToBase64Url()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Public key to verify signatures only
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Serializable
 | 
				
			||||||
 | 
					    @SerialName("p")
 | 
				
			||||||
 | 
					    class Public(override val packed: UByteArray) : SigningKey() {
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Verify the signature and return true if it is correct.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        fun verify(signature: UByteArray, message: UByteArray): Boolean = try {
 | 
				
			||||||
 | 
					            Signature.verifyDetached(signature, message, packed)
 | 
				
			||||||
 | 
					            true
 | 
				
			||||||
 | 
					        } catch (_: InvalidSignatureException) {
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        override fun toString(): String = "Pub:${super.toString()}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Secret key to sign only
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Serializable
 | 
				
			||||||
 | 
					    @SerialName("s")
 | 
				
			||||||
 | 
					    class Secret(override val packed: UByteArray) : SigningKey() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val publicKey: Public by lazy {
 | 
				
			||||||
 | 
					            Public(Signature.ed25519SkToPk(packed))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun sign(message: UByteArray): UByteArray = Signature.detached(message, packed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun seal(message: UByteArray): Seal = Seal(this.publicKey, sign(message))
 | 
				
			||||||
 | 
					        override fun toString(): String = "Sct:${super.toString()}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        companion object {
 | 
				
			||||||
 | 
					            data class Pair(val signing: Secret, val aPublic: Public)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fun pair(): Pair {
 | 
				
			||||||
 | 
					                val p = Signature.keypair()
 | 
				
			||||||
 | 
					                return Pair(Secret(p.secretKey), Public(p.publicKey))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IllegalSignatureException: RuntimeException("signed data is tampered or signature is corrupted")
 | 
				
			||||||
							
								
								
									
										7
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/contrail.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/contrail.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.sergeych.bintools.CRC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun isValidContrail(data: UByteArray): Boolean = CRC.crc8(data.copyOfRange(1, data.size)) == data[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun createContrail(data: UByteArray): UByteArray = ubyteArrayOf(CRC.crc8(data)) + data
 | 
				
			||||||
							
								
								
									
										111
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/tools.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/tools.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.secretbox.SecretBox
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.secretbox.crypto_secretbox_NONCEBYTES
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.util.LibsodiumRandom
 | 
				
			||||||
 | 
					import kotlinx.coroutines.channels.ReceiveChannel
 | 
				
			||||||
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 | 
					import net.sergeych.bintools.toDataSource
 | 
				
			||||||
 | 
					import net.sergeych.bipack.BipackDecoder
 | 
				
			||||||
 | 
					import net.sergeych.bipack.BipackEncoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DecryptionFailedException : RuntimeException("can't encrypt: wrong key or tampered message")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Serializable
 | 
				
			||||||
 | 
					data class WithNonce(
 | 
				
			||||||
 | 
					    val cipherData: UByteArray,
 | 
				
			||||||
 | 
					    val nonce: UByteArray,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Serializable
 | 
				
			||||||
 | 
					data class WithFill(
 | 
				
			||||||
 | 
					    val data: UByteArray,
 | 
				
			||||||
 | 
					    val safetyFill: UByteArray? = null
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    constructor(data: UByteArray, fillSize: Int) : this(data, randomBytes(fillSize))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun readVarUnsigned(input: ReceiveChannel<UByte>): UInt {
 | 
				
			||||||
 | 
					    var result = 0u
 | 
				
			||||||
 | 
					    var cnt = 0
 | 
				
			||||||
 | 
					    while(true) {
 | 
				
			||||||
 | 
					        val b = input.receive().toUInt()
 | 
				
			||||||
 | 
					        result = (result shl 7) or (b and 0x7fu)
 | 
				
			||||||
 | 
					        if( (b and 0x80u) != 0u ) {
 | 
				
			||||||
 | 
					            return result
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if( ++cnt > 5 ) throw IllegalArgumentException("overflow while decoding varuint")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun encodeVarUnsigned(value: UInt): UByteArray {
 | 
				
			||||||
 | 
					    val result = mutableListOf<UByte>()
 | 
				
			||||||
 | 
					    var rest = value
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        val mask = if( rest <= 0x7fu ) 0x80u else 0u
 | 
				
			||||||
 | 
					        result.add( (mask or (rest and 0x7fu)).toUByte() )
 | 
				
			||||||
 | 
					        rest = rest shr 7
 | 
				
			||||||
 | 
					    } while(rest != 0u)
 | 
				
			||||||
 | 
					    return result.toUByteArray()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun randomBytes(n: Int): UByteArray = if (n > 0) LibsodiumRandom.buf(n) else ubyteArrayOf()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun randomBytes(n: UInt): UByteArray = if (n > 0u) LibsodiumRandom.buf(n.toInt()) else ubyteArrayOf()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Uniform random in `0 ..< max` range
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun randomUInt(max: UInt) = LibsodiumRandom.uniform(max)
 | 
				
			||||||
 | 
					fun randomUInt(max: Int) = LibsodiumRandom.uniform(max.toUInt())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T: Comparable<T>>T.limit(range: ClosedRange<T>) = when {
 | 
				
			||||||
 | 
					    this < range.start -> range.start
 | 
				
			||||||
 | 
					    this > range.endInclusive -> range.endInclusive
 | 
				
			||||||
 | 
					    else -> this
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T: Comparable<T>>T.limitMax(max: T) = if( this < max ) this else max
 | 
				
			||||||
 | 
					fun <T: Comparable<T>>T.limitMin(min: T) = if( this > min ) this else min
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun randomNonce(): UByteArray = randomBytes(crypto_secretbox_NONCEBYTES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Secret-key encrypt with authentication.
 | 
				
			||||||
 | 
					 * Generates random nonce and add some random fill to protect
 | 
				
			||||||
 | 
					 * against some analysis attacks. Nonce is included in the result. To be
 | 
				
			||||||
 | 
					 * used with [decrypt].
 | 
				
			||||||
 | 
					 * @param secretKey a _secret_ key, see [SecretBox.keygen()] or like.
 | 
				
			||||||
 | 
					 * @param plain data to encrypt
 | 
				
			||||||
 | 
					 * @param fillSize number of random fill data to add. Use random value or default.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun encrypt(
 | 
				
			||||||
 | 
					    secretKey: UByteArray,
 | 
				
			||||||
 | 
					    plain: UByteArray,
 | 
				
			||||||
 | 
					    fillSize: Int = randomUInt((plain.size * 3 / 10).limitMin(3)).toInt()
 | 
				
			||||||
 | 
					): UByteArray {
 | 
				
			||||||
 | 
					    val filled = BipackEncoder.encode(WithFill(plain, fillSize))
 | 
				
			||||||
 | 
					    val nonce = randomNonce()
 | 
				
			||||||
 | 
					    val encrypted = SecretBox.easy(filled.toUByteArray(), nonce, secretKey)
 | 
				
			||||||
 | 
					    return BipackEncoder.encode(WithNonce(encrypted, nonce)).toUByteArray()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Decrypt a secret-key-based message, normally encrypted with [encrypt].
 | 
				
			||||||
 | 
					 * @throws DecryptionFailedException if the key is wrong or a message is tampered with (MAC
 | 
				
			||||||
 | 
					 *          check failed).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun decrypt(secretKey: UByteArray, cipher: UByteArray): UByteArray {
 | 
				
			||||||
 | 
					    val wn: WithNonce = BipackDecoder.decode(cipher.toDataSource())
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        return BipackDecoder.decode<WithFill>(
 | 
				
			||||||
 | 
					            SecretBox.openEasy(wn.cipherData, wn.nonce, secretKey).toDataSource()
 | 
				
			||||||
 | 
					        ).data
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    catch(_: com.ionspin.kotlin.crypto.secretbox.SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey) {
 | 
				
			||||||
 | 
					        throw DecryptionFailedException()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/utools.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/commonMain/kotlin/net/sergeych/crypto2/utools.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.sergeych.crypto2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.sergeych.bintools.toDump
 | 
				
			||||||
 | 
					import net.sergeych.mp_tools.encodeToBase64Url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun UByteArray.toDump(wide: Boolean = false) = toByteArray().toDump(wide)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun UByteArray.encodeToBase64Url(): String = toByteArray().encodeToBase64Url()
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/commonMain/kotlin/net/sergeych/tools/AtomicCounter.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/commonMain/kotlin/net/sergeych/tools/AtomicCounter.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package net.sergeych.tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Suppress("unused")
 | 
				
			||||||
 | 
					class AtomicCounter(initialValue: Long = 0) {
 | 
				
			||||||
 | 
					 private val op = ProtectedOp()
 | 
				
			||||||
 | 
					    var value: Long = initialValue
 | 
				
			||||||
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun incrementAndGet(): Long = op { ++value }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/commonMain/kotlin/net/sergeych/tools/ProtectedOp.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/commonMain/kotlin/net/sergeych/tools/ProtectedOp.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					package net.sergeych.tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Multiplatform interface to perform a regular (not suspend) operation
 | 
				
			||||||
 | 
					 * protected by a platform mutex (where necessary). Get real implementation
 | 
				
			||||||
 | 
					 * with [ProtectedOp]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					interface ProtectedOpImplementation {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Call [f] iin mutually exclusive mode, it means that only one invocation
 | 
				
			||||||
 | 
					     * can be active at a time, all the rest are waiting until the current operation
 | 
				
			||||||
 | 
					     * will finish.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    operator fun <T>invoke(f: ()->T): T
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get the platform-depended implementation of a mutex-protected operation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					expect fun ProtectedOp(): ProtectedOpImplementation
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/commonMain/kotlin/net/sergeych/tools/flow_tools.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/commonMain/kotlin/net/sergeych/tools/flow_tools.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.sergeych.tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.coroutines.cancel
 | 
				
			||||||
 | 
					import kotlinx.coroutines.coroutineScope
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.Flow
 | 
				
			||||||
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * suspend until the flow produces the value to which the
 | 
				
			||||||
 | 
					 * predicate returns true
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					suspend fun <T>Flow<T>.waitFor(predicate: (T)->Boolean) {
 | 
				
			||||||
 | 
					    coroutineScope {
 | 
				
			||||||
 | 
					        launch {
 | 
				
			||||||
 | 
					            collect {
 | 
				
			||||||
 | 
					                if( predicate(it) ) cancel()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/commonMain/kotlin/net/sergeych/utools/collections.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/commonMain/kotlin/net/sergeych/utools/collections.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.sergeych.utools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Scan the collection and return the first non-null result of the [predicate] on it.
 | 
				
			||||||
 | 
					 * If all the elements give null with predicate call, returns null.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note that collection is scanned only to the first non-null predicate result.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun <T,R>Collection<T>.firstNonNull(predicate: (T)->R?): R? {
 | 
				
			||||||
 | 
					    for( x in this ) predicate(x)?.let { return it }
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/commonMain/kotlin/net/sergeych/utools/packing.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/commonMain/kotlin/net/sergeych/utools/packing.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package net.sergeych.utools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.serialization.KSerializer
 | 
				
			||||||
 | 
					import kotlinx.serialization.serializer
 | 
				
			||||||
 | 
					import net.sergeych.bintools.toDataSource
 | 
				
			||||||
 | 
					import net.sergeych.bipack.BipackDecoder
 | 
				
			||||||
 | 
					import net.sergeych.bipack.BipackEncoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Effectively pack anyk nullable object. The result could be effectively packed
 | 
				
			||||||
 | 
					 * in turn as a part of a more complex structure.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * To avoid packing non-null mark,
 | 
				
			||||||
 | 
					 * we use a zero-size array, which, if in turn encoded, packs into a single
 | 
				
			||||||
 | 
					 * zero byte. Thus, we avoid extra byte spending for unnecessary null
 | 
				
			||||||
 | 
					 * check.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					inline fun <reified T> pack(element: T?): UByteArray = pack(serializer<T>(), element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Unpack nullable data packed with [pack]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					inline fun <reified T: Any?> unpack(encoded: UByteArray): T =
 | 
				
			||||||
 | 
					    unpack(serializer<T>(), encoded)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Effectively pack anyk nullable object. The result could be effectively packed
 | 
				
			||||||
 | 
					 * in turn as a part of a more complex structure.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * To avoid packing non-null mark,
 | 
				
			||||||
 | 
					 * we use a zero-size array, which, if in turn encoded, packs into a single
 | 
				
			||||||
 | 
					 * zero byte. Thus, we avoid extra byte spending for unnecessary null
 | 
				
			||||||
 | 
					 * check.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun <T>pack(serializer: KSerializer<T>, element: T?): UByteArray =
 | 
				
			||||||
 | 
					    if (element == null) ubyteArrayOf()
 | 
				
			||||||
 | 
					    else BipackEncoder.encode(serializer,element).toUByteArray()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Unpack nullable data packed with [pack]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Suppress("UNCHECKED_CAST")
 | 
				
			||||||
 | 
					fun <T: Any?> unpack(serializer: KSerializer<T>, encoded: UByteArray): T =
 | 
				
			||||||
 | 
					    if (encoded.isEmpty()) null as T
 | 
				
			||||||
 | 
					    else BipackDecoder.decode(encoded.toByteArray().toDataSource(),serializer)
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/commonMain/kotlin/net/sergeych/utools/time.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/commonMain/kotlin/net/sergeych/utools/time.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.sergeych.utools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.datetime.Clock
 | 
				
			||||||
 | 
					import kotlinx.datetime.Instant
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun now(): Instant = Clock.System.now()
 | 
				
			||||||
 | 
					fun nowToSeconds(): Instant = Clock.System.now().truncateToSeconds()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun Instant.truncateToSeconds(): Instant =
 | 
				
			||||||
 | 
					    Instant.fromEpochSeconds(toEpochMilliseconds()/1000)
 | 
				
			||||||
							
								
								
									
										183
									
								
								src/commonMain/kotlin/org/komputing/khash/keccak/Keccak.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/commonMain/kotlin/org/komputing/khash/keccak/Keccak.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					package org.komputing.khash.keccak
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.bignum.integer.BigInteger
 | 
				
			||||||
 | 
					import org.komputing.khash.keccak.extensions.fillWith
 | 
				
			||||||
 | 
					import kotlin.math.min
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					object Keccak {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val BIT_65 = BigInteger.ONE shl (64)
 | 
				
			||||||
 | 
					    private val MAX_64_BITS = BIT_65 - BigInteger.ONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun digest(value: ByteArray, parameter: KeccakParameter): ByteArray {
 | 
				
			||||||
 | 
					        val uState = IntArray(200)
 | 
				
			||||||
 | 
					        val uMessage = convertToUInt(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var blockSize = 0
 | 
				
			||||||
 | 
					        var inputOffset = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Absorbing phase
 | 
				
			||||||
 | 
					        while (inputOffset < uMessage.size) {
 | 
				
			||||||
 | 
					            blockSize = min(uMessage.size - inputOffset, parameter.rateInBytes)
 | 
				
			||||||
 | 
					            for (i in 0 until blockSize) {
 | 
				
			||||||
 | 
					                uState[i] = uState[i] xor uMessage[i + inputOffset]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            inputOffset += blockSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (blockSize == parameter.rateInBytes) {
 | 
				
			||||||
 | 
					                doF(uState)
 | 
				
			||||||
 | 
					                blockSize = 0
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Padding phase
 | 
				
			||||||
 | 
					        uState[blockSize] = uState[blockSize] xor parameter.d
 | 
				
			||||||
 | 
					        if (parameter.d and 0x80 != 0 && blockSize == parameter.rateInBytes - 1) {
 | 
				
			||||||
 | 
					            doF(uState)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uState[parameter.rateInBytes - 1] = uState[parameter.rateInBytes - 1] xor 0x80
 | 
				
			||||||
 | 
					        doF(uState)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Squeezing phase
 | 
				
			||||||
 | 
					        val byteResults = mutableListOf<Byte>()
 | 
				
			||||||
 | 
					        var tOutputLen = parameter.outputLengthInBytes
 | 
				
			||||||
 | 
					        while (tOutputLen > 0) {
 | 
				
			||||||
 | 
					            blockSize = min(tOutputLen, parameter.rateInBytes)
 | 
				
			||||||
 | 
					            for (i in 0 until blockSize) {
 | 
				
			||||||
 | 
					                byteResults.add(uState[i].toByte().toInt().toByte())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tOutputLen -= blockSize
 | 
				
			||||||
 | 
					            if (tOutputLen > 0) {
 | 
				
			||||||
 | 
					                doF(uState)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return byteResults.toByteArray()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun doF(uState: IntArray) {
 | 
				
			||||||
 | 
					        val lState = Array(5) { Array(5) { BigInteger.ZERO } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (i in 0..4) {
 | 
				
			||||||
 | 
					            for (j in 0..4) {
 | 
				
			||||||
 | 
					                val data = IntArray(8)
 | 
				
			||||||
 | 
					                val index = 8 * (i + 5 * j)
 | 
				
			||||||
 | 
					                uState.copyInto(data, 0, index, index + data.size)
 | 
				
			||||||
 | 
					                lState[i][j] = convertFromLittleEndianTo64(data)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        roundB(lState)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uState.fillWith(0)
 | 
				
			||||||
 | 
					        for (i in 0..4) {
 | 
				
			||||||
 | 
					            for (j in 0..4) {
 | 
				
			||||||
 | 
					                val data = convertFrom64ToLittleEndian(lState[i][j])
 | 
				
			||||||
 | 
					                data.copyInto(uState, 8 * (i + 5 * j))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Permutation on the given state.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private fun roundB(state: Array<Array<BigInteger>>) {
 | 
				
			||||||
 | 
					        var lfsrState = 1
 | 
				
			||||||
 | 
					        for (round in 0..23) {
 | 
				
			||||||
 | 
					            val c = arrayOfNulls<BigInteger>(5)
 | 
				
			||||||
 | 
					            val d = arrayOfNulls<BigInteger>(5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // θ step
 | 
				
			||||||
 | 
					            for (i in 0..4) {
 | 
				
			||||||
 | 
					                c[i] = state[i][0].xor(state[i][1]).xor(state[i][2]).xor(state[i][3]).xor(state[i][4])
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (i in 0..4) {
 | 
				
			||||||
 | 
					                d[i] = c[(i + 4) % 5]!!.xor(c[(i + 1) % 5]!!.leftRotate64(1))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (i in 0..4) {
 | 
				
			||||||
 | 
					                for (j in 0..4) {
 | 
				
			||||||
 | 
					                    state[i][j] = state[i][j].xor(d[i]!!)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // ρ and π steps
 | 
				
			||||||
 | 
					            var x = 1
 | 
				
			||||||
 | 
					            var y = 0
 | 
				
			||||||
 | 
					            var current = state[x][y]
 | 
				
			||||||
 | 
					            for (i in 0..23) {
 | 
				
			||||||
 | 
					                val tX = x
 | 
				
			||||||
 | 
					                x = y
 | 
				
			||||||
 | 
					                y = (2 * tX + 3 * y) % 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                val shiftValue = current
 | 
				
			||||||
 | 
					                current = state[x][y]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                state[x][y] = shiftValue.leftRotate64Safely((i + 1) * (i + 2) / 2)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // χ step
 | 
				
			||||||
 | 
					            for (j in 0..4) {
 | 
				
			||||||
 | 
					                val t = arrayOfNulls<BigInteger>(5)
 | 
				
			||||||
 | 
					                for (i in 0..4) {
 | 
				
			||||||
 | 
					                    t[i] = state[i][j]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (i in 0..4) {
 | 
				
			||||||
 | 
					                    // ~t[(i + 1) % 5]
 | 
				
			||||||
 | 
					                    val invertVal = t[(i + 1) % 5]!!.xor(MAX_64_BITS)
 | 
				
			||||||
 | 
					                    // t[i] ^ ((~t[(i + 1) % 5]) & t[(i + 2) % 5])
 | 
				
			||||||
 | 
					                    state[i][j] = t[i]!!.xor(invertVal.and(t[(i + 2) % 5]!!))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // ι step
 | 
				
			||||||
 | 
					            for (i in 0..6) {
 | 
				
			||||||
 | 
					                lfsrState = (lfsrState shl 1 xor (lfsrState shr 7) * 0x71) % 256
 | 
				
			||||||
 | 
					                // pow(2, i) - 1
 | 
				
			||||||
 | 
					                val bitPosition = (1 shl i) - 1
 | 
				
			||||||
 | 
					                if (lfsrState and 2 != 0) {
 | 
				
			||||||
 | 
					                    state[0][0] = state[0][0].xor(BigInteger.ONE shl bitPosition)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts the given [data] array to an [IntArray] containing UInt values.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private fun convertToUInt(data: ByteArray) = IntArray(data.size) {
 | 
				
			||||||
 | 
					        data[it].toInt() and 0xFF
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts the given [data] array containing the little endian representation of a number to a [BigInteger].
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private fun convertFromLittleEndianTo64(data: IntArray): BigInteger {
 | 
				
			||||||
 | 
					        val value = data.map { it.toString(16) }
 | 
				
			||||||
 | 
					            .map { if (it.length == 2) it else "0$it" }
 | 
				
			||||||
 | 
					            .reversed()
 | 
				
			||||||
 | 
					            .joinToString("")
 | 
				
			||||||
 | 
					        return BigInteger.parseString(value, 16)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts the given [BigInteger] to a little endian representation as an [IntArray].
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private fun convertFrom64ToLittleEndian(uLong: BigInteger): IntArray {
 | 
				
			||||||
 | 
					        val asHex = uLong.toString(16)
 | 
				
			||||||
 | 
					        val asHexPadded = "0".repeat((8 * 2) - asHex.length) + asHex
 | 
				
			||||||
 | 
					        return IntArray(8) {
 | 
				
			||||||
 | 
					            ((7 - it) * 2).let { pos ->
 | 
				
			||||||
 | 
					                asHexPadded.substring(pos, pos + 2).toInt(16)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun BigInteger.leftRotate64Safely(rotate: Int) = leftRotate64(rotate % 64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun BigInteger.leftRotate64(rotate: Int) = (this shr (64 - rotate)).add(this shl rotate).mod(BIT_65)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.komputing.khash.keccak
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Parameters defining the FIPS 202 standard.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum class KeccakParameter(val rateInBytes: Int,val outputLengthInBytes: Int, val d: Int) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    KECCAK_224(144, 28, 0x01),
 | 
				
			||||||
 | 
					    KECCAK_256(136, 32, 0x01),
 | 
				
			||||||
 | 
					    KECCAK_384(104, 48, 0x01),
 | 
				
			||||||
 | 
					    KECCAK_512(72, 64, 0x01),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SHA3_224(144, 28, 0x06),
 | 
				
			||||||
 | 
					    SHA3_256(136, 32, 0x06),
 | 
				
			||||||
 | 
					    SHA3_384(104, 48, 0x06),
 | 
				
			||||||
 | 
					    SHA3_512(72, 64, 0x06),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SHAKE128(168, 32, 0x1F),
 | 
				
			||||||
 | 
					    SHAKE256(136, 64, 0x1F)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					package org.komputing.khash.keccak.extensions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Assigns the specified int value to each element of the specified
 | 
				
			||||||
 | 
					 * range in the specified array of ints. The range to be filled
 | 
				
			||||||
 | 
					 * extends from index <tt>fromIndex</tt>, inclusive, to index
 | 
				
			||||||
 | 
					 * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
 | 
				
			||||||
 | 
					 * range to be filled is empty.)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param fromIndex the index of the first element (inclusive) to be
 | 
				
			||||||
 | 
					 *        filled with the specified value
 | 
				
			||||||
 | 
					 * @param toIndex the index of the last element (exclusive) to be
 | 
				
			||||||
 | 
					 *        filled with the specified value
 | 
				
			||||||
 | 
					 * @param value the value to be stored in all elements of the array
 | 
				
			||||||
 | 
					 * @throws IllegalArgumentException if <tt>fromIndex > toIndex</tt>
 | 
				
			||||||
 | 
					 * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex < 0</tt> or
 | 
				
			||||||
 | 
					 *         <tt>toIndex > a.length</tt>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					internal fun IntArray.fillWith(value: Int, fromIndex: Int = 0, toIndex: Int = this.size) {
 | 
				
			||||||
 | 
					    if (fromIndex > toIndex) {
 | 
				
			||||||
 | 
					        throw IllegalArgumentException(
 | 
				
			||||||
 | 
					            "fromIndex($fromIndex) > toIndex($toIndex)"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (fromIndex < 0) {
 | 
				
			||||||
 | 
					        throw ArrayIndexOutOfBoundsException(fromIndex)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (toIndex > this.size) {
 | 
				
			||||||
 | 
					        throw ArrayIndexOutOfBoundsException(toIndex)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i in fromIndex until toIndex)
 | 
				
			||||||
 | 
					        this[i] = value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Constructs a new [ArrayIndexOutOfBoundsException]
 | 
				
			||||||
 | 
					 * class with an argument indicating the illegal index.
 | 
				
			||||||
 | 
					 * @param index the illegal index.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					internal class ArrayIndexOutOfBoundsException(index: Int) : Throwable("Array index out of range: $index")
 | 
				
			||||||
@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					@file:Suppress("unused")
 | 
				
			||||||
 | 
					package org.komputing.khash.keccak.extensions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.komputing.khash.keccak.Keccak
 | 
				
			||||||
 | 
					import org.komputing.khash.keccak.KeccakParameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Computes the proper Keccak digest of [this] byte array based on the given [parameter]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun ByteArray.digestKeccak(parameter: KeccakParameter): ByteArray {
 | 
				
			||||||
 | 
					    return Keccak.digest(this, parameter)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Computes the proper Keccak digest of [this] string based on the given [parameter]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun String.digestKeccak(parameter: KeccakParameter): ByteArray {
 | 
				
			||||||
 | 
					    return Keccak.digest(encodeToByteArray(), parameter)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										58
									
								
								src/commonTest/kotlin/KeysTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/commonTest/kotlin/KeysTest.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					import com.ionspin.kotlin.crypto.secretbox.SecretBox
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.util.decodeFromUByteArray
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.util.encodeToUByteArray
 | 
				
			||||||
 | 
					import kotlinx.coroutines.test.runTest
 | 
				
			||||||
 | 
					import net.sergeych.crypto2.*
 | 
				
			||||||
 | 
					import net.sergeych.utools.pack
 | 
				
			||||||
 | 
					import net.sergeych.utools.unpack
 | 
				
			||||||
 | 
					import kotlin.test.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KeysTest {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testCreationAndMap() = runTest {
 | 
				
			||||||
 | 
					        initCrypto()
 | 
				
			||||||
 | 
					        val (stk,pbk) = SigningKey.Secret.pair()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val x = mapOf( stk to "STK!", pbk to "PBK!")
 | 
				
			||||||
 | 
					        assertEquals("STK!", x[stk])
 | 
				
			||||||
 | 
					        val s1 = SigningKey.Secret(stk.packed)
 | 
				
			||||||
 | 
					        assertEquals(stk, s1)
 | 
				
			||||||
 | 
					        assertEquals("STK!", x[s1])
 | 
				
			||||||
 | 
					        assertEquals("PBK!", x[pbk])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val data = "8 rays dev!".encodeToUByteArray()
 | 
				
			||||||
 | 
					        val data1 = "8 rays dev!".encodeToUByteArray()
 | 
				
			||||||
 | 
					        val s = stk.seal(data)
 | 
				
			||||||
 | 
					        assertTrue(s.verify(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data1[0] = 0x01u
 | 
				
			||||||
 | 
					        assertFalse(s.verify(data1))
 | 
				
			||||||
 | 
					        val p2 = SigningKey.Secret.pair()
 | 
				
			||||||
 | 
					        val p3 = SigningKey.Secret.pair()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val ms = SignedBox(data, s1) + p2.signing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // non tampered:
 | 
				
			||||||
 | 
					        val ms1 = unpack<SignedBox>(pack(ms))
 | 
				
			||||||
 | 
					        assertContentEquals(data, ms1.message)
 | 
				
			||||||
 | 
					        assertTrue(pbk in ms1)
 | 
				
			||||||
 | 
					        assertTrue(p2.aPublic in ms1)
 | 
				
			||||||
 | 
					        assertTrue(p3.aPublic !in ms1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertThrows<IllegalSignatureException> {
 | 
				
			||||||
 | 
					            unpack<SignedBox>(pack(ms).also { it[3] = 1u })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun secretEncryptTest() = runTest {
 | 
				
			||||||
 | 
					        initCrypto()
 | 
				
			||||||
 | 
					        val key = SecretBox.keygen()
 | 
				
			||||||
 | 
					        val key1 = SecretBox.keygen()
 | 
				
			||||||
 | 
					        assertEquals("hello", decrypt(key, encrypt(key, "hello".encodeToUByteArray())).decodeFromUByteArray())
 | 
				
			||||||
 | 
					        assertThrows<DecryptionFailedException> {
 | 
				
			||||||
 | 
					            decrypt(key, encrypt(key1, "hello".encodeToUByteArray())).decodeFromUByteArray()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/commonTest/kotlin/PackTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/commonTest/kotlin/PackTest.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import kotlinx.coroutines.test.runTest
 | 
				
			||||||
 | 
					import kotlinx.datetime.Instant
 | 
				
			||||||
 | 
					import net.sergeych.crypto2.initCrypto
 | 
				
			||||||
 | 
					import net.sergeych.utools.nowToSeconds
 | 
				
			||||||
 | 
					import net.sergeych.utools.pack
 | 
				
			||||||
 | 
					import net.sergeych.utools.unpack
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertEquals
 | 
				
			||||||
 | 
					import kotlin.time.Duration.Companion.microseconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PackTest {
 | 
				
			||||||
 | 
					        inline fun <reified T>check(x: T?) {
 | 
				
			||||||
 | 
					            assertEquals(x, unpack<T>(pack(x)))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testNullPack() = runTest {
 | 
				
			||||||
 | 
					        initCrypto()
 | 
				
			||||||
 | 
					        val d = pack("Hello")
 | 
				
			||||||
 | 
					        assertEquals(6, d.size)
 | 
				
			||||||
 | 
					        check(1)
 | 
				
			||||||
 | 
					        check(2L)
 | 
				
			||||||
 | 
					        check(1.00)
 | 
				
			||||||
 | 
					        check("hello")
 | 
				
			||||||
 | 
					        check<String>(null)
 | 
				
			||||||
 | 
					        check<Long?>(null)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testTimePack() = runTest {
 | 
				
			||||||
 | 
					        initCrypto()
 | 
				
			||||||
 | 
					        val t1 = nowToSeconds()
 | 
				
			||||||
 | 
					        val t2 = t1 + 1.microseconds
 | 
				
			||||||
 | 
					        assertEquals(t1, unpack<Instant>(pack(t2)))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/commonTest/kotlin/ToolsTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/commonTest/kotlin/ToolsTest.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					import kotlinx.coroutines.test.runTest
 | 
				
			||||||
 | 
					import net.sergeych.crypto2.createContrail
 | 
				
			||||||
 | 
					import net.sergeych.crypto2.initCrypto
 | 
				
			||||||
 | 
					import net.sergeych.crypto2.isValidContrail
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertEquals
 | 
				
			||||||
 | 
					import kotlin.test.assertFalse
 | 
				
			||||||
 | 
					import kotlin.test.assertTrue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ToolsTest {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testContrails() = runTest {
 | 
				
			||||||
 | 
					        initCrypto()
 | 
				
			||||||
 | 
					        val c = createContrail(ubyteArrayOf(1u, 2u, 3u, 4u, 5u))
 | 
				
			||||||
 | 
					        assertEquals(134u, c[0])
 | 
				
			||||||
 | 
					        assertTrue { isValidContrail(c) }
 | 
				
			||||||
 | 
					        c[2] = 11u
 | 
				
			||||||
 | 
					        assertFalse { isValidContrail(c) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/commonTest/kotlin/assertThrows.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/commonTest/kotlin/assertThrows.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					import kotlin.test.fail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline fun <reified T: Throwable>assertThrows(f: ()->Unit): T {
 | 
				
			||||||
 | 
					    val name = T::class.simpleName
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        f()
 | 
				
			||||||
 | 
					        fail("expected to throw $name but threw nothing")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    catch(x: Throwable) {
 | 
				
			||||||
 | 
					        if( x is T ) return x
 | 
				
			||||||
 | 
					        fail("expected to throw $name but instead threw ${x::class.simpleName}: $x")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/jsMain/kotlin/net/sergeych/tools/ProtectedOp.js.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/jsMain/kotlin/net/sergeych/tools/ProtectedOp.js.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					package net.sergeych.tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actual fun ProtectedOp(): ProtectedOpImplementation = object : ProtectedOpImplementation {
 | 
				
			||||||
 | 
					    // JS targets are inherently single-threaded, so we do noting:
 | 
				
			||||||
 | 
					    override fun <T> invoke(f: () -> T): T = f()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/jvmMain/kotlin/net/sergeych/tools/ProtectedOp.jvm.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/jvmMain/kotlin/net/sergeych/tools/ProtectedOp.jvm.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package net.sergeych.tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actual fun ProtectedOp(): ProtectedOpImplementation = object : ProtectedOpImplementation {
 | 
				
			||||||
 | 
					    private val lock = Object()
 | 
				
			||||||
 | 
					    override fun <T> invoke(f: () -> T): T {
 | 
				
			||||||
 | 
					        synchronized(lock) { return f() }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					package net.sergeych.tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.atomicfu.locks.SynchronizedObject
 | 
				
			||||||
 | 
					import kotlinx.atomicfu.locks.synchronized
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actual fun ProtectedOp(): ProtectedOpImplementation = object : ProtectedOpImplementation {
 | 
				
			||||||
 | 
					    private val lock = SynchronizedObject()
 | 
				
			||||||
 | 
					    override fun <T> invoke(f: () -> T): T {
 | 
				
			||||||
 | 
					        synchronized(lock) {
 | 
				
			||||||
 | 
					            return f()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user