crypto = require 'crypto'
ursa = require 'ursa'
msgpack = require 'msgpack'
stream = require 'stream'
keyLib = require './key'
Buffer.prototype.toArray = -> Array.prototype.slice.call this, 0crypto = require 'crypto'
ursa = require 'ursa'
msgpack = require 'msgpack'
stream = require 'stream'
keyLib = require './key'
Buffer.prototype.toArray = -> Array.prototype.slice.call this, 0Standard and recommended class for encrypting and authenticating stream-like data.
keys is a hash of key-related data.  May contain the following  fields:key - A random symmetric key, usually through
caesar.key.createRandom().  If present, this key will be used on all
messages.  If not present, a random key will be created for each message.public - An array or hash of public keys.  If a hash, the key’s owner
can be identified easier (by anybody—not just the owner).  If present, a
keyring will be available to the owners of the corresponding private keys
that allows them to read the message without any previous knowledge of
the symmetric key.private - An array or hash of private keys.  If a hash, the key’s owner
can be identified easier (by anybody—not just the owner).  If present, 
asymmetric authentication can be used (signatures), which is publicly
verifiable.confidential is whether or not the data should be encrypted to prevent
eavesdropping.  (Boolean)integrous is the type of integrity that should be maintained.  Can be
null (no integrity), “sym” (symmetric integrity, verifiable by others with
the secret key), and “asym” (asymmetric integrity, verifiable by anyone).
(String)cut is the maximum size of a plaintext chunk (where it should be cut).
If you get strange errors, try lowering this below the default.  (Number)class exports.Encrypter extends stream.Transform
    constructor: (@keys, @confidential, @integrous, @cut = 14336) ->
        if not this instanceof exports.Encrypter
            return new exports.Encrypter @keys, @confidential, @integrous, @cut
        
        stream.Transform.call this, objectMode: true, decodeStrings: true
        @leftover = new Buffer 0
        
        if @confidential isnt true and @confidential isnt false
            throw 'Confidential must be true or false.'
        
        if @integrous? and @integrous isnt 'sym' and @integrous isnt 'asym'
            throw 'Integrous must be null, sym, or asym.'
        
        requiresKey = @confidential or @integrous is 'sym'
        if requiresKey and not @keys.key? and not @keys.public?
            throw 'No symmetric or public key.'
        
        if @integrous is 'asym' and not @keys.private?
            throw 'No private key to sign messages with.'
        
        if not @confidential then @buffer = new stream.PassThrough()
        else if @keys.key?
            @buffer = crypto.createCipher 'aes-256-ctr', @keys.key
    
    _transform: (dump, encoding, done) ->
        while dump.length isnt 0 # Chop up dump.
            len = if dump.length > @cut then @cut else dump.length
            chunk = new Buffer len
            dump.copy chunk
            dump = dump.slice len
            
            if not @keys.key? # Generate random key if needed.
                key = keyLib.createRandom()
                @buffer = crypto.createCipher 'aes-256-ctr', key
            else key = @keys.key
            
            @buffer.write chunk
            data = @buffer.read()
            footer = {}
            
            if @integrous is 'sym' # Calculate HMAC.
                mac = crypto.createHmac 'sha512', key
                mac.end data
                footer.mac = mac.read().toArray()
            
            if @integrous is 'asym' # Calculate signature.
                sigs = {}
                footer.sigs = if @keys.private instanceof Array then [] else {}
                sigs[k] = ursa.createSigner 'sha512' for k of @keys.private
                sigs[k].update data for k of @keys.private
                footer.sigs[k] = sigs[k].sign v for k, v of @keys.private
                footer.sigs[k] = v.toArray() for k, v of footer.sigs
            
            if @keys.public? # Calculate keyring.
                footer.keys = if @keys.public instanceof Array then [] else {}
                footer.keys[k] = v.encrypt key for k, v of @keys.public
                footer.keys[k] = v.toArray() for k, v of footer.keys
            
            footer = msgpack.pack footer
            out = new Buffer 4 + data.length + footer.length
            out.writeUInt16BE data.length, 0
            data.copy out, 2, 0, data.length
            out.writeUInt16BE footer.length, data.length + 2
            footer.copy out, data.length + 4, 0, footer.length
            
            more = @push out
            if not more then return done()
        
        done()
    
    _flush: (done) ->
        @push if @leftover.length is 0 then null else @leftover
        @leftover = new Buffer 0
        done()Standard and recommended class for decrypting and verifying stream-like data.
keys is a hash of key-related data.  May contain the following  fields:key - A random symmetric key, usually through
caesar.key.createRandom().  If present, this key will be used on all
messages.  If not present, a keyring must be present to decrypt messages.public - An array or hash of public keys.  If a hash, the key’s owner
can be identified easier.  If present, they will be used to verify
signatures.private - An array or hash of private keys.  If a hash, the key’s owner 
can be identified easier.  If present, they will be used to derive
symmetric keys from a keyring.confidential is whether or not the data should be encrypted to prevent
eavesdropping.  Must match the Encrypter’s value.  (Boolean)integrous is the type of integrity that should be maintained.  Can be
null (no integrity), “sym” (symmetric integrity, verifiable by others with
the secret key), and “asym” (asymmetric integrity, verifiable by anyone).
Must match the Encrypter’s value.  (String)class exports.Decrypter extends stream.Transform
    constructor: (@keys, @confidential, @integrous) ->
        if not this instanceof exports.Decrypter
            return new exports.Decrypter @keys, @confidential, @integrous
        
        stream.Transform.call this, decodeStrings: true
        
        if @confidential isnt true and @confidential isnt false
            throw 'Confidential must be true or false.'
        
        if @integrous? and @integrous isnt 'sym' and @integrous isnt 'asym'
            throw 'Integrous must be null, sym, or asym.'
        
        requiresKey = @confidential or @integrous is 'sym'
        if requiresKey and not @keys.key? and not @keys.private?
            throw 'No symmetric or private key.'
        
        if @keys.key? and @confidential
            @cipher = crypto.createDecipher 'aes-256-ctr', @keys.key
        else @cipher = new stream.PassThrough()
    
    _transform: (chunk, encoding, done) ->
        dlen = chunk.readUInt16BE 0
        data = chunk.slice 2, 2 + dlen
        
        flen = chunk.readUInt16BE 2 + dlen
        footer = msgpack.unpack chunk.slice 4 + dlen
        
        if not data? then return done 'No payload.'
        if not footer.mac? and @integrous is 'sym' then return done 'No MAC.'
        if not footer.sigs? and @integrous is 'asym' then return done 'No sigs.'
        
        requiresKey = @confidential or @integrous is 'sym'
        if requiresKey and not @keys.key? and not footer.keys?
            return done 'No method of key derivation.'
        
        key = null
        if @keys.key? # Derive key if needed.
            key = @keys.key
        else
            if footer.keys instanceof Array
                for tag in footer.keys
                    for n, privKey of @keys.private
                        try key = privKey.decrypt tag
                        catch err then key = null
                        
                        if key? then break
                    
                    if key? then break
            else
                keys = (key for key of @keys.private when footer.keys[key]?)
                if keys.length is 0
                    return done 'No valid keys for decryption.'
                
                for k in keys
                    try key = @keys.private[k].decrypt new Buffer footer.keys[k]
                    catch err then key = null
                    
                    if key? then break
        
        if not key? then return done 'No key was successfully derived.'
        
        if not @keys.key? # Intialize new cipher if needed.
            @cipher = crypto.createDecipher 'aes-256-ctr', key
        
        if @integrous is 'sym' # Verify an HMAC.
            mac = crypto.createHmac 'sha512', key
            mac.end new Buffer footer.mac
            candidateTag = mac.read().toString 'base64'
            
            mac = crypto.createHmac 'sha512', key
            mac.end data
            tag = mac.read()
            
            mac = crypto.createHmac 'sha512', key
            mac.end tag
            tag = mac.read().toString 'base64'
            
            if candidateTag isnt tag then return done 'Bad MAC.'
        
        if @integrous is 'asym' # Verify a signature.
            ok = false
            if footer.sigs instanceof Array
                for sig in footer.sigs
                    sig = new Buffer sig
                    for pubKey in @keys.public
                        v = ursa.createVerifier 'sha512'
                        v.update data
                        ok = v.verify pubKey, sig, 'base64'
                        if ok then break
                    
                    if ok then break
            else
                keys = (key for key of @keys.public when footer.sigs[key]?)
                if keys.length is 0 then return done 'No valid keys for auth.'
                
                for k in keys
                    try
                        v = ursa.createVerifier 'sha512'
                        v.update data
                        ok = v.verify @keys.public[k], new Buffer footer.sigs[k]
                        if ok then break
                    catch e
            
            if not ok then return done 'Bad signature.'
        
        @cipher.write data # Decrypt the message.
        @push @cipher.read()
        done()An implementation of Synthetic IV encryption. SIV is a method of deterministic and authenticated encryption, commonly used in encrypted databases and insecure keystores. This is because given data can be encrypted and used in a search for other related SIV encrypted information which can then be decrypted (unlike with a hash, for example). Because SIV is deterministic, only use it on data whose structure prevents repetition (like user ids, usernames, or randomly generated keys).
key1 - Randomly generated symmetric key, usually through 
caesar.key.createRandom().key2 - See above.  Should be different from key1.class exports.SIVEncrypter extends stream.Transform
    constructor: (@key1, @key2) ->
        if not this instanceof exports.SIVEncrypter
            return new exports.SIVEncrypter @key1, @key2
        
        stream.Transform.call this, objectMode: true, decodeStrings: true
        @leftover = new Buffer 0
    
    _transform: (dump, encoding, done) ->
        while dump.length isnt 0 # Chop up dump.
            len = if dump.length > 16368 then 16368 else dump.length
            chunk = new Buffer len
            dump.copy chunk
            dump = dump.slice len
            
            hash = crypto.createHash 'sha256' # Calculate IV.
            hash.end chunk
            tag = hash.read()
            
            temp = crypto.createCipher 'aes-256-ctr', @key1
            temp.end tag
            iv = temp.read().slice 0, 16
            
            cipher = crypto.createCipheriv 'aes-256-ctr', @key2, iv # Encrypt.
            cipher.end chunk
            end = Buffer.concat [iv, cipher.read()]
            more = @push end
            if not more then return done()
        
        done()
    
    _flush: (done) ->
        @push if @leftover.length is 0 then null else @leftover
        @leftover = new Buffer 0
        done()An implementation of Synthetic IV decryption. See above.
class exports.SIVDecrypter extends stream.Transform
    constructor: (@key1, @key2) ->
        if not this instanceof exports.SIVDecrypter
            return new exports.SIVDecrypter @key1, @key2
        
        stream.Transform.call this, decodeStrings: true
    
    _transform: (chunk, encoding, done) ->
        iv = chunk.slice 0, 16
        data = chunk.slice 16
        
        decipher = crypto.createDecipheriv 'aes-256-ctr', @key2, iv # Decrypt
        decipher.end data
        pt = decipher.read()
        
        hash = crypto.createHash 'sha256' # Authenticate by calculating IV
        hash.end pt
        tag = hash.read()
        
        temp = crypto.createCipher 'aes-256-ctr', @key1
        temp.end tag
        ivCand = temp.read().slice 0, 16
        
        if iv.toString('base64') isnt ivCand.toString('base64')
            return done 'Failed auth.'
        
        @push pt
        done()An implementation of XTS encryption. XTS is size-preserving encryption used for randomly accessible data, like RAM or hard disks, which are split into sectors of fixed size. Because size must be preserved, no authentication can be used, meaning any ciphertext can be altered by an attacker and will still decrypt.
key - Randomly generated symmetric key, usually through 
caesar.key.createRandom().cut - The size in bytes of each sector.  If the size of the plaintext is 
not divisible by the cut then 0x00s are appended.class exports.XTSEncrypter extends stream.Transform
    constructor: (@key, @cut = 32) ->
        if not this instanceof exports.XTSEncrypter
            return new exports.XTSEncrypter @key, @cut
        
        stream.Transform.call this, decodeStrings: true
        @cipher = crypto.createCipher 'aes-256-xts', @key
    
    _transform: (dump, encoding, done) ->
        while dump.length isnt 0 # Chop up dump.
            chunk = new Buffer @cut
            dump.copy chunk
            dump = dump.slice @cut
            
            @cipher.write chunk
            @push @cipher.read()
        
        done()An implementation of XTS decryption. See above.
class exports.XTSDecrypter extends stream.Transform
    constructor: (@key, @cut = 32) ->
        if not this instanceof exports.XTSDecrypter
            return new exports.XTSDecrypter @key
        
        stream.Transform.call this, decodeStrings: true
        @decipher = crypto.createDecipher 'aes-256-xts', @key
    
    _transform: (dump, encoding, done) ->
        while dump.length isnt 0 # Chop up dump.
            chunk = new Buffer @cut
            dump.copy chunk
            dump = dump.slice @cut
            
            @decipher.write chunk
            @push @decipher.read()
        
        done()