Last active
June 25, 2024 23:44
-
-
Save andimiller/ece21f767e02d335316a01b50244b26d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.scalanative.unsafe.* | |
import scala.scalanative.unsigned.* | |
// Raw bindings | |
@link("sodium") | |
@extern | |
object libsodium { | |
def sodium_init(): CInt = extern | |
// sizes | |
def crypto_sign_publickeybytes(): CInt = extern | |
def crypto_sign_secretkeybytes(): CInt = extern | |
def crypto_sign_ed25519_bytes(): CInt = extern | |
def crypto_sign_keypair(pk: Ptr[Byte], sk: Ptr[Byte]): CInt = extern | |
def crypto_sign_detached(sig: Ptr[Byte], siglen_p: Ptr[ULong], m: Ptr[Byte], mlen: ULong, sk: Ptr[Byte]): CInt = extern | |
def crypto_sign_verify_detached(sig: Ptr[Byte], m: Ptr[Byte], mlen: ULong, pk: Ptr[Byte]): CInt = extern | |
} | |
// Low level bindings | |
object sodium { | |
class SodiumCouldNotInitialise extends Throwable("libsodium's sodium_init() has failed, the library cannot be used") | |
def generateKeyPair(using Zone): (Array[Byte], Array[Byte]) = if (libsodium.sodium_init() >= 0) { | |
val pk = new Array[Byte](libsodium.crypto_sign_publickeybytes()) | |
val sk = new Array[Byte](libsodium.crypto_sign_secretkeybytes()) | |
assert(libsodium.crypto_sign_keypair(pk.at(0), sk.at(0)) == 0) | |
(pk, sk) | |
} else { | |
throw new SodiumCouldNotInitialise() | |
} | |
def signDetached(message: Array[Byte], signKey: Array[Byte])(using Zone): Array[Byte] = if (libsodium.sodium_init() >= 0) { | |
val sigByteCount = libsodium.crypto_sign_ed25519_bytes() | |
val signature = new Array[Byte](sigByteCount) | |
libsodium.crypto_sign_detached(signature.at(0), null, message.at(0), message.length.toULong, signKey.at(0)) | |
signature | |
} else { | |
throw new SodiumCouldNotInitialise() | |
} | |
def verifyDetached(signature: Array[Byte], message: Array[Byte], publicKey: Array[Byte])(using Zone): Int = if ( | |
libsodium.sodium_init() >= 0 | |
) { | |
libsodium.crypto_sign_verify_detached(signature.at(0), message.at(0), message.length.toULong, publicKey.at(0)).toInt | |
} else { | |
throw new SodiumCouldNotInitialise() | |
} | |
} | |
import scodec.bits.ByteVector | |
// OO Bindings | |
object nacl { | |
opaque type PublicKey = ByteVector | |
object PublicKey: | |
def fromHex(s: String): Option[PublicKey] = ByteVector.fromHex(s) | |
opaque type SecretKey = ByteVector | |
object SecretKey: | |
def fromHex(s: String): Option[SecretKey] = ByteVector.fromHex(s) | |
extension (sk: SecretKey) | |
def sign(data: ByteVector): Signature = Zone { implicit z => | |
ByteVector(sodium.signDetached(data.toArray, sk.toArray)) | |
} | |
opaque type Signature = ByteVector | |
object Signature: | |
def fromHex(s: String): Option[Signature] = ByteVector.fromHex(s) | |
extension (sig: Signature) | |
def isValid(data: ByteVector, pk: PublicKey): Boolean = Zone { implicit z => | |
sodium.verifyDetached(sig.toArray, data.toArray, pk.toArray) == 0 | |
} | |
def generateKeyPair(): (PublicKey, SecretKey) = Zone { implicit z => | |
val (pk, sk) = sodium.generateKeyPair | |
(ByteVector(pk), ByteVector(sk)) | |
} | |
} | |
import cats.effect.* | |
import cats.Show | |
// FP bindings | |
object sodium4s { | |
opaque type PublicKey = ByteVector | |
object PublicKey: | |
def fromHex(s: String): Option[PublicKey] = ByteVector.fromHex(s) | |
given Show[PublicKey] = _.toHex | |
opaque type SecretKey = ByteVector | |
object SecretKey: | |
def fromHex(s: String): Option[SecretKey] = ByteVector.fromHex(s) | |
given Show[SecretKey] = _.toHex | |
opaque type Signature = ByteVector | |
object Signature: | |
def fromHex(s: String): Option[Signature] = ByteVector.fromHex(s) | |
given Show[Signature] = _.toHex | |
trait Sodium[F[_]] { | |
def generateKeyPair(): F[(PublicKey, SecretKey)] | |
def sign(sk: SecretKey)(data: ByteVector): F[Signature] | |
def validate(pk: PublicKey)(sig: Signature)(data: ByteVector): F[Boolean] | |
} | |
object Sodium { | |
def create[F[_]: Sync] = new Sodium[F] { | |
def generateKeyPair(): F[(PublicKey, SecretKey)] = | |
Sync[F].delay { | |
Zone { implicit z => | |
val (pk, sk) = sodium.generateKeyPair | |
(ByteVector(pk), ByteVector(sk)) | |
} | |
} | |
def sign(sk: SecretKey)(data: ByteVector): F[Signature] = | |
Sync[F].delay { | |
Zone { implicit z => | |
ByteVector(sodium.signDetached(data.toArray, sk.toArray)) | |
} | |
} | |
def validate(pk: PublicKey)(sig: Signature)(data: ByteVector): F[Boolean] = | |
Sync[F].delay { | |
Zone { implicit z => | |
sodium.verifyDetached(sig.toArray, data.toArray, pk.toArray) == 0 | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment