API Docs for: 0.2.0
Show:

File: src/DecryptionCipher.coffee

###
This file is part of the Lockbox package.

Copyright © 2013 Erin Millard

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
###

DecryptionFailedException = require './Exception/DecryptionFailedException'
InvalidPrivateKeyException = require './Exception/InvalidPrivateKeyException'

###*
# The standard Lockbox decryption cipher.
#
# @class lockbox.DecryptionCipher
###
module.exports = class DecryptionCipher

  ###*
  # @class lockbox.DecryptionCipher
  # @constructor
  #
  # @param {crypto} [crypto] The cryptography module to use.
  # @param {ursa}   [ursa]   The Ursa module to use.
  ###
  constructor: (crypto = (require 'crypto'), ursa = (require 'ursa')) ->
    @_crypto = crypto
    @_ursa = ursa

  ###*
  # Decrypt a data packet.
  #
  # Throws:
  #
  #   - {{#crossLink "lockbox.exception.DecryptionFailedException"}}
  #       lockbox.exception.DecryptionFailedException
  #     {{/crossLink}}
  #     If the decryption failed.
  #
  # @method decrypt
  #
  # @param {ursa.Key}      key  The key to decrypt with.
  # @param {String|Buffer} data The data to decrypt.
  #
  # @return {Buffer} The decrypted data.
  # @throws {lockbox.exception.DecryptionFailedException} If the decryption failed.
  ###
  decrypt: (key, data) ->
    try
      @_ursa.assertPrivateKey key
    catch error
      error = new InvalidPrivateKeyException key, error
      throw new DecryptionFailedException error

    data = @_base64UriDecode data

    keyAndIv = data.slice 0, key.getModulus().length
    try
      keyAndIv = key.decrypt keyAndIv
    catch error
      throw new DecryptionFailedException error

    generatedKey = keyAndIv.slice 0, 32
    throw new DecryptionFailedException if generatedKey.length isnt 32

    iv = keyAndIv.slice 32
    throw new DecryptionFailedException if iv.length isnt 16

    data = data.slice key.getModulus().length
    try
      data = @_decryptAes generatedKey, iv, data
    catch error
      throw new DecryptionFailedException error

    verificationDigest = data.slice(-20).toString 'binary'
    data = data.slice 0, -20

    hash = @_crypto.createHash 'sha1'
    hash.update data
    digest = hash.digest().toString 'binary'

    throw new DecryptionFailedException if digest isnt verificationDigest

    return data

  _decryptAes: (key, iv, data) ->
    cipher = @_crypto.createDecipheriv 'aes-256-cbc', key, iv
    Buffer.concat [cipher.update(data), cipher.final()]

  _base64UriDecode: (data) ->
    data = data.toString 'binary' if Buffer.isBuffer data
    data = data.replace(/-/g, '+').replace(/_/g, '/')
    new Buffer data, 'base64'