All files utils.mjs

47.62% Statements 20/42
20% Branches 2/10
50% Functions 3/6
50% Lines 19/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155                                                                                                                            15x 15x 15x 15x                                               1x                           1x 6x 6x         6x 6x           1x 6x 6x 6x 6x 6x 6x 6x 6x                                                          
// const base64url = require('base64url')
// const sodium = require('sodium-universal')
// const hcrypto = require('hypercore-crypto')
// const { default: parseJwk } = require('jose')('./jwk/parse')
// const { default: CompactSign } = require('jose')('./jws/compact/sign')
// const { default: compactVerify } = require('jose')('./jws/compact/verify')
// JSON.canonicalize = require('canonicalize')
 
import parseJwk from 'jose/jwk/parse'
import CompactSign from 'jose/jws/compact/sign'
import compactVerify from 'jose/jws/compact/verify'
 
import base64url from 'base64url'
import canonicalize from 'canonicalize'
import sodium from 'sodium-universal'
 
// const encodeJson = payload =>
//   base64url.encode(Buffer.from(JSON.stringify(payload)))
 
// const decodeJson = encodedPayload =>
//   JSON.parse(base64url.decode(encodedPayload))
 
// const signEncodedPayload = async (encodedHeader, encodedPayload, privateKey) => {
//   const toBeSigned = `${encodedHeader}.${encodedPayload}`
//   const hash = hashIt(toBeSigned)
//   const privateKeyBuffer = Buffer.from(privateKey, 'hex')
//   const signature = hcrypto.sign(hash, privateKeyBuffer)
//   const encoding = 'hex'
//   const signature64 = base64url.encode(signature.toString(encoding), encoding)
//   return signature64
// }
 
// const makeSignedOperation = async (header, payload, privateKey) => {
//   const encodedHeader = encodeJson(header)
//   const encodedPayload = encodeJson(payload)
//   const signature = await signEncodedPayload(
//     encodedHeader,
//     encodedPayload,
//     privateKey
//   )
//   const operation = {
//     header: encodedHeader, // used to be called protected, but header makes more sense
//     payload: encodedPayload,
//     signature
//   }
//   return operation
// }
 
// const verifyOperationSignature = (
//   encodedHeader,
//   encodedPayload,
//   signature,
//   publicKey
// ) => {
//   const toBeVerified = `${encodedHeader}.${encodedPayload}`
//   const hash = hashIt(toBeVerified)
//   const publicKeyBuffer = Buffer.from(publicKey, 'hex')
//   // verify(message, signature, publicKey) // verify the signature of this value matches the public key under which it was published
//   return hcrypto.verify(hash, base64url.toBuffer(signature), publicKeyBuffer)
// }
 
function hashIt (data) {
  const dataBuffer = Buffer.from(data)
  const hash = Buffer.allocUnsafe(sodium.crypto_generichash_BYTES)
  sodium.crypto_generichash(hash, dataBuffer)
  return hash
}
 
// privateKey must be in KeyLike format
async function getCompactJWS (data, secretKey, alg = 'EdDSA') {
  if (!Buffer.isBuffer(secretKey)) secretKey = Buffer.from(secretKey, 'hex')
 
  const privateKeyJwk = privateKeyJwkFromEd25519PrivateKey(secretKey)
 
  const privateKeyLike = await parseJwk({ ...privateKeyJwk, alg }) // Returns: Promise<KeyLike>
  console.log()
 
  // Compact JWS Signature
  const encoder = new TextEncoder()
 
  if (data instanceof Object) data = JSON.stringify(data) // needs to be a string
 
  const jwsCompact = await new CompactSign(encoder.encode(data))
    .setProtectedHeader({ alg })
    .sign(privateKeyLike)
  return jwsCompact
}
 
// private JWK is simply the private key as d and x
const privateKeyJwkFromEd25519PrivateKey = (Ed25519privateKey) => {
  const jwk = {
    crv: 'Ed25519',
    d: base64url.encode(Ed25519privateKey.slice(0, 32)),
    x: base64url.encode(Ed25519privateKey.slice(32, 64)),
    kty: 'OKP'
  }
  const kid = getKid(jwk)
  return {
    ...jwk,
    kid
  }
}
 
const publicKeyJwkFromPublicKey = (Ed25519publicKey) => {
  if (!Buffer.isBuffer(Ed25519publicKey)) Ed25519publicKey = Buffer.from(Ed25519publicKey, 'hex')
  const jwk = {
    crv: 'Ed25519',
    x: base64url.encode(Ed25519publicKey),
    kty: 'OKP'
  }
  const kid = getKid(jwk)
  return {
    ...jwk,
    kid
  }
}
 
const getKid = (jwk) => {
  const copy = { ...jwk }
  delete copy.d
  delete copy.kid
  delete copy.alg
  const digest = Buffer.alloc(sodium.crypto_generichash_BYTES)
  const uint8array = new TextEncoder('utf-16').encode(canonicalize(copy))
  sodium.crypto_generichash(digest, uint8array) // blake2b-256
  return base64url.encode(Buffer.from(digest))
}
 
async function validatePayload (jwsCompact, publicKeyJwk, alg = 'EdDSA') {
  // Compact verify
  const decoder = new TextDecoder()
  try {
    const publicKeyLike = await parseJwk({ ...publicKeyJwk, alg })
    const { payload, protectedHeader } = await compactVerify(jwsCompact, publicKeyLike)
    if (!payload) return false
    // console.log(protectedHeader)
    // console.log(decoder.decode(payload))
    // verify success
    return JSON.parse(decoder.decode(payload))
  } catch (error) {
    // verify failed
    console.error(error)
    return false
  }
}
 
// module.exports
export {
  compactVerify,
  publicKeyJwkFromPublicKey,
  getCompactJWS,
  validatePayload,
  hashIt
}