2017-12-06 22:56:09 +13:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const async = require('async')
|
|
|
|
const forge = require('node-forge')
|
|
|
|
const util = require('./util')
|
|
|
|
|
|
|
|
class CMS {
|
|
|
|
constructor (keystore) {
|
|
|
|
if (!keystore) {
|
|
|
|
throw new Error('keystore is required')
|
|
|
|
}
|
|
|
|
|
2017-12-07 00:10:22 +13:00
|
|
|
this.keystore = keystore
|
2017-12-06 22:56:09 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
createAnonymousEncryptedData (name, plain, callback) {
|
|
|
|
const self = this
|
|
|
|
if (!Buffer.isBuffer(plain)) {
|
|
|
|
return callback(new Error('Data is required'))
|
|
|
|
}
|
|
|
|
|
|
|
|
self.keystore._getPrivateKey(name, (err, key) => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const privateKey = forge.pki.decryptRsaPrivateKey(key, self.keystore._())
|
|
|
|
util.certificateForKey(privateKey, (err, certificate) => {
|
|
|
|
if (err) return callback(err)
|
|
|
|
|
|
|
|
// create a p7 enveloped message
|
|
|
|
const p7 = forge.pkcs7.createEnvelopedData()
|
|
|
|
p7.addRecipient(certificate)
|
|
|
|
p7.content = forge.util.createBuffer(plain)
|
|
|
|
p7.encrypt()
|
|
|
|
|
|
|
|
// convert message to DER
|
|
|
|
const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
|
|
|
|
callback(null, Buffer.from(der, 'binary'))
|
|
|
|
})
|
|
|
|
} catch (err) {
|
|
|
|
callback(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
readData (cmsData, callback) {
|
|
|
|
if (!Buffer.isBuffer(cmsData)) {
|
|
|
|
return callback(new Error('CMS data is required'))
|
|
|
|
}
|
|
|
|
|
|
|
|
const self = this
|
|
|
|
let cms
|
|
|
|
try {
|
2017-12-07 00:10:22 +13:00
|
|
|
const buf = forge.util.createBuffer(cmsData.toString('binary'))
|
2017-12-06 22:56:09 +13:00
|
|
|
const obj = forge.asn1.fromDer(buf)
|
|
|
|
cms = forge.pkcs7.messageFromAsn1(obj)
|
|
|
|
} catch (err) {
|
|
|
|
return callback(new Error('Invalid CMS: ' + err.message))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find a recipient whose key we hold. We only deal with recipient certs
|
|
|
|
// issued by ipfs (O=ipfs).
|
|
|
|
const recipients = cms.recipients
|
|
|
|
.filter(r => r.issuer.find(a => a.shortName === 'O' && a.value === 'ipfs'))
|
|
|
|
.filter(r => r.issuer.find(a => a.shortName === 'CN'))
|
|
|
|
.map(r => {
|
|
|
|
return {
|
|
|
|
recipient: r,
|
|
|
|
keyId: r.issuer.find(a => a.shortName === 'CN').value
|
|
|
|
}
|
|
|
|
})
|
|
|
|
async.detect(
|
|
|
|
recipients,
|
|
|
|
(r, cb) => self.keystore.findKeyById(r.keyId, (err, info) => cb(null, !err && info)),
|
|
|
|
(err, r) => {
|
|
|
|
if (err) return callback(err)
|
|
|
|
if (!r) return callback(new Error('No key found for decryption'))
|
|
|
|
|
|
|
|
async.waterfall([
|
|
|
|
(cb) => self.keystore.findKeyById(r.keyId, cb),
|
|
|
|
(key, cb) => self.keystore._getPrivateKey(key.name, cb)
|
|
|
|
], (err, pem) => {
|
2017-12-07 00:10:22 +13:00
|
|
|
if (err) return callback(err)
|
2017-12-06 22:56:09 +13:00
|
|
|
|
|
|
|
const privateKey = forge.pki.decryptRsaPrivateKey(pem, self.keystore._())
|
|
|
|
cms.decrypt(r.recipient, privateKey)
|
|
|
|
async.setImmediate(() => callback(null, Buffer.from(cms.content.getBytes(), 'binary')))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = CMS
|