How to get a Digest object from raw Data

Hi there

TL;DR : I have a Data object which contains data that is already hashed. I need a Digest object, how should I proceed ?

I am developing an OSX Smart Card Token Extension to handle certificates linked to private keys in the Secure Enclave (using CryptoKit).

So far my first tests are pretty successful as my extension already answered to various signature requests successfully... until now.

So far I was receiving signature requests for ecdsaSignatureMessageX962SHA256 algorithm.

All I had to do with was something like this:

func tokenSession(_ session: TKTokenSession, sign dataToSign: Data, keyObjectID: Any, algorithm: TKTokenKeyAlgorithm) throws -> Data {
if let privateKey = try? SecureEnclave.P256.Signing.PrivateKey.init(dataRepresentation: keyObjectID as! Data) {
      let rawsignature = try? privateKey.signature(for: dataToSign)
      return rawsignature!.derRepresentation
    }
}

Now I receive requests for ecdsaSignatureDigestX962SHA256 signatures. I noticed that there is a

public func signature<D>(for digest: D) throws -> P256.Signing.ECDSASignature where D : Digest

function that can be called but in the tokenSession i am only given Data... Looking at SHA256Digest documentation I can't find anything to create the digest from bytes. It seems that it can only be the result of a SHA256.hash operation.

I thought of using older API like SecKeyCreateSignature but I don't think I can retrieve a SecKey from a private key generated with CryptoKit SecureEnclave.P256.Signing.PrivateKey.init

I feel like I may be missing something really simple...

I managed to do it by creating my own class of Digest:

public struct MyDigest : Digest {
  let bytes: (UInt64, UInt64, UInt64, UInt64)
   
  public static var byteCount: Int = 32
   
  public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
    return try Swift.withUnsafeBytes(of: bytes) {
      let boundsCheckedPtr = UnsafeRawBufferPointer(start: $0.baseAddress,
                             count: Self.byteCount)
      return try body(boundsCheckedPtr)
    }
  }
   
  public func hash(into hasher: inout Hasher) {
    self.withUnsafeBytes { hasher.combine(bytes: $0) }
  }
   
  init?(bufferPointer: UnsafeRawBufferPointer) {
    guard bufferPointer.count == 32 else {
      return nil
    }

    var bytes = (UInt64(0), UInt64(0), UInt64(0), UInt64(0))
    withUnsafeMutableBytes(of: &bytes) { targetPtr in
      targetPtr.copyMemory(from: bufferPointer)
    }
    self.bytes = bytes
  }
   
  func toArray() -> ArraySlice<UInt8> {
      var array = [UInt8]()
      array.appendByte(bytes.0)
      array.appendByte(bytes.1)
      array.appendByte(bytes.2)
      array.appendByte(bytes.3)
      return array.prefix(upTo: SHA256Digest.byteCount)
    }
}

extension MutableDataProtocol {
  mutating func appendByte(_ byte: UInt64) {
    withUnsafePointer(to: byte.littleEndian, { self.append(contentsOf: UnsafeRawBufferPointer(start: $0, count: 8)) })
  }
}

(thank you world of open source )

Then I used it by passing my input data as an UnsafeRawBufferPointer

  fileprivate func signADigest(digestToSignAsData: Data, keyObjectID: Any) -> P256.Signing.ECDSASignature {
    var signature: P256.Signing.ECDSASignature?
    let dataAsBufferPointer : UnsafeRawBufferPointer = digestToSignAsData.withUnsafeBytes {
      print("-")
      return $0
       
    }
    let dataAsDigest = MyDigest(bufferPointer: dataAsBufferPointer)!
    if let privateKey = try? SecureEnclave.P256.Signing.PrivateKey.init(dataRepresentation: keyObjectID as! Data) {
      signature = try? privateKey.signature(for: dataAsDigest)
    }
    return signature!
  }

To make sur that it works I changed the method where I used to sign messages. Instead of calling privateKey.signature(for: data), I make the digest myself with SHA256.hash(data: dataToSign) and then call my method that handle digest signature. I then call privateKey.publicKey.isValidSignature(digestSignature, for: data) on input data to check validity.

So everything is fine, but coding my own Digest by almost copying the source code of SHA256Digest feels more like a hack than a clean solution. If anybody has a better way of doing this I would love to hear it.

Hi @aruffin!

The digest can just be produced by invoking the hash function, this can be done in the following way:

let digest = SHA256.hash(data: data)

Can you explain why it would not be possible for you to re-hash the data? Blindly signing hashes can be concerning from a security perspective as the signer does not validate the data that is signed as part of the hash.

How to get a Digest object from raw Data
 
 
Q