In cryptography, loads of operations happen on bytes and strings, thus, tsec naturally includes some helpers for this.

importing tsec.common._ brings in:

String syntax

    import org.apache.commons.codec.binary.{Base64 => AB64}
    def utf8Bytes: Array[Byte]                              = s.getBytes(StandardCharsets.UTF_8)
    def asciiBytes: Array[Byte]                             = s.getBytes(StandardCharsets.US_ASCII)
    def base64Bytes: Array[Byte]                            = Base64.getDecoder.decode(s)
    def base64UrlBytes: Array[Byte]                         = AB64.decodeBase64(s)
    def hexBytes[F[_]](implicit F: Sync[F]): F[Array[Byte]] = F.delay(Hex.decodeHex(s))
    def hexBytesUnsafe: Array[Byte]                         = Hex.decodeHex(s)
    def toStringRepr[A](implicit stringEV: StringEV[A]): A  = stringEV.fromString(s)
scala> import tsec.common._
import tsec.common._

scala> "hi".utf8Bytes //Get the utf-8 bytes of a string
res0: Array[Byte] = Array(104, 105)

scala> "FF0952".hexBytesUnsafe //Note: This can throw!
res1: Array[Byte] = Array(-1, 9, 82)

Note: Hex encoding provided by apache commons io

Bytes syntax

    def toUtf8String                             = new String(array, StandardCharsets.UTF_8)
    def toAsciiString                            = new String(array, StandardCharsets.US_ASCII)
    def toB64UrlString: String                   = AB64.encodeBase64URLSafeString(array)
    def toB64String: String                      = Base64.getEncoder.encodeToString(array)
    def toHexString: String                      = Hex.encodeHexString(array)
    def toRepr[A](implicit byteEV: ByteEV[A]): A = byteEV.fromArray(array)
scala> "hi".utf8Bytes.toHexString
res2: String = 6869

scala> "hello".asciiBytes.toB64UrlString
res3: String = aGVsbG8

Secure Random Ids

tsec provides a SecureRandomId which is simply a string random bytes generated by java’s SecureRandom, then hex encoded. The SecureRandom implementation is managed for proper security and reseeding (see the ManagedRandom section).

The type itself is a newtype using cats.evidence.Is

To generate SecureRandomIds of more than 32 bits, you can simply set the length in a custom SecureRandomIdGenerator, with the signature

case class SecureRandomIdGenerator(sizeInBytes: Int = 32) extends ManagedRandom {
  def generate: SecureRandomId


Proper SecureRandom management requires occasional reseeding. Extending ManagedRandom in your class will effectively provide a nextBytes method which is identical in function to SecureRandom’s nextBytes, but will reseed periodically (though sensibly, not too often), managing concurrency with a LongAdder. Though note: nextBytes is a side effecting function that mutates the array, this is not managed in a functional style to keep an API identical to java’s SecureRandom, but it would not be hard to implement the effect management, if you so desire it.

SecureRandomId, and many other secure random constructs in tsec are implemented using ManagedRandom to manage proper reseeding.