All Downloads are FREE. Search and download functionalities are using the official Maven repository.

crypto.Signer.scala Maven / Gradle / Ivy

The newest version!
//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//:
//:   Licensed under the Apache License, Version 2.0 (the "License");
//:   you may not use this file except in compliance with the License.
//:   You may obtain a copy of the License at
//:
//:       http://www.apache.org/licenses/LICENSE-2.0
//:
//:   Unless required by applicable law or agreed to in writing, software
//:   distributed under the License is distributed on an "AS IS" BASIS,
//:   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//:   See the License for the specific language governing permissions and
//:   limitations under the License.
//:
//: ----------------------------------------------------------------------------
 package nelson
package crypto

import AuthFailure._
import scodec.bits.ByteVector
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec


import scalaz.\/

final class SignatureKey private(val bytes: ByteVector) {
  lazy val keySpec: java.security.Key =
    new SecretKeySpec(bytes.toArray, Hmac.algorithm)
}

object SignatureKey {
  val minBytes: Int = 8

  def apply(bytes: ByteVector): InsufficientSignatureKeyLength \/ SignatureKey =
    if (bytes.length >= minBytes) \/.right(new SignatureKey(bytes))
    else \/.left(InsufficientSignatureKeyLength(actual = bytes.length, required = minBytes.toLong))

  def unsafe(bytes: ByteVector): SignatureKey =
    apply(bytes).valueOr(e => throw new IllegalArgumentException(e.toString))
}

object Hmac {
  val algorithm: String = "HmacSHA256"

  def mkHmac(): Mac = {
    // Note we want to spell out SunJCE here to prevent possible errors when apps
    // using a conflicting security provider like LunaHSM.
    val hmac = Mac.getInstance(algorithm, "SunJCE")
    hmac
  }
}

/**
 * A `Signer` computes a signature (such as a checksum) of data.
 *
 * @tparam F The context in which results are wrapped. This allows a Signer
 *  to return a possible failure via Option, a disjunction, etc.
 */
abstract class Signer[F[_]] {
  def signature(key: SignatureKey, data: ByteVector, signatureLengthBytes: Int): F[ByteVector]
}

/**
 * An HMAC-based implementation of [[Signer]] that caches `Mac` instances to
 * reduce the overhead of initialization.
 *
 * Caching a per-key `Mac` instead of just a thread-local `Mac` would remove
 * the need to initialize the mac on each signature, but benchmarks show that
 * it doesn't make a significant performance difference.
 */
final class SafeHolderHmac(holder: SafeHolder[Mac]) extends Signer[AuthResult] {

  def signature(key: SignatureKey, data: ByteVector, signatureLengthBytes: Int): AuthResult[ByteVector] = {
    try {
      val hmac = holder.getOrCreate(() => Hmac.mkHmac())
      hmac.init(key.keySpec)
      hmac.update(data.toArray)
      val sig = hmac.doFinal
      val actualLength = sig.size
      // this might be able to be slightly optimized
      if (actualLength >= signatureLengthBytes) \/.right(ByteVector.view(sig).take(signatureLengthBytes.toLong))
      else \/.left(InsufficientSignatureLength(signatureLengthBytes.toLong, actualLength.toLong))
    } catch {
      case e: Exception =>
        \/.left(SigningError(key.bytes.length, data.length, signatureLengthBytes.toLong, e))
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy