From 6586acc9b117380a89a389e9c802fa961be2be73 Mon Sep 17 00:00:00 2001 From: Daniel Santos Date: Thu, 1 Nov 2018 16:51:19 -0300 Subject: [PATCH] Doing the manual versioning of the lib --- dist/browser/SecureRandom.js | 2 +- dist/browser/creds/VerifiableCredential.js | 2 +- dist/browser/creds/definitions.js | 2 +- dist/browser/isClaimRelated.js | 2 +- dist/browser/services/index.js | 2 +- dist/browser/uca/UserCollectableAttribute.js | 2 +- dist/browser/uca/definitions.js | 2 +- dist/cjs/SecureRandom.js | 36 +++++++----- dist/cjs/creds/VerifiableCredential.js | 39 +++++++++---- dist/cjs/creds/definitions.js | 4 +- dist/cjs/isClaimRelated.js | 2 +- dist/cjs/services/httpService.js | 2 +- dist/cjs/services/index.js | 18 ++++-- dist/cjs/uca/UserCollectableAttribute.js | 60 +++++++++++++------- dist/cjs/uca/definitions.js | 22 +++---- dist/es/SecureRandom.js | 36 +++++++----- dist/es/creds/VerifiableCredential.js | 39 +++++++++---- dist/es/creds/definitions.js | 4 +- dist/es/isClaimRelated.js | 2 +- dist/es/services/httpService.js | 2 +- dist/es/services/index.js | 18 ++++-- dist/es/uca/UserCollectableAttribute.js | 60 +++++++++++++------- dist/es/uca/definitions.js | 22 +++---- package.json | 2 +- 24 files changed, 244 insertions(+), 138 deletions(-) diff --git a/dist/browser/SecureRandom.js b/dist/browser/SecureRandom.js index 66ecd85e..3b5812fa 100644 --- a/dist/browser/SecureRandom.js +++ b/dist/browser/SecureRandom.js @@ -1 +1 @@ -"use strict";var _classCallCheck2=require("babel-runtime/helpers/classCallCheck"),_classCallCheck3=_interopRequireDefault(_classCallCheck2),_createClass2=require("babel-runtime/helpers/createClass"),_createClass3=_interopRequireDefault(_createClass2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var sjcl=require("sjcl"),logger=require("./logger"),SecureRandomizer=function(){function a(){(0,_classCallCheck3.default)(this,a),logger.debug("Init Secure Randon"),this.sjclRandom=new sjcl.prng(10);try{logger.debug("Trying crypto");var b=require("crypto").randomBytes(1024).toString("hex"),c=sjcl.codec.hex.toBits(b);this.sjclRandom.addEntropy(c,void 0,"csprng"),this.isSeeded=!0}catch(a){logger.warn("Crypto: "+a),this.isSeeded=!1}}return(0,_createClass3.default)(a,[{key:"wordWith",value:function(a){if(!this.isSeeded)throw new Error("Can't user SecureRandon before seeding");var b=this.sjclRandom.randomWords(a/8,10);return sjcl.codec.hex.fromBits(b)}}]),a}();module.exports=new SecureRandomizer; \ No newline at end of file +"use strict";var _classCallCheck2=require("babel-runtime/helpers/classCallCheck"),_classCallCheck3=_interopRequireDefault(_classCallCheck2),_createClass2=require("babel-runtime/helpers/createClass"),_createClass3=_interopRequireDefault(_createClass2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var sjcl=require("sjcl"),logger=require("./logger"),SecureRandom=function(){function a(b){if((0,_classCallCheck3.default)(this,a),logger.debug("Init Secure Random"),this.sjclRandom=new sjcl.prng(10),b){var c=sjcl.codec.hex.toBits(b);this.sjclRandom.addEntropy(c,void 0,"csprng"),this.isSeeded=!0}else try{logger.debug("Trying crypto");var d=require("crypto").randomBytes(1024).toString("hex"),e=sjcl.codec.hex.toBits(d);this.sjclRandom.addEntropy(e,void 0,"csprng"),this.isSeeded=!0}catch(a){logger.warn("Crypto: "+a),this.isSeeded=!1}}return(0,_createClass3.default)(a,[{key:"wordWith",value:function(a){if(!this.isSeeded)throw new Error("Can't user SecureRandom before seeding");var b=this.sjclRandom.randomWords(a/8,10);return sjcl.codec.hex.fromBits(b)}}]),a}();module.exports=SecureRandom; \ No newline at end of file diff --git a/dist/browser/creds/VerifiableCredential.js b/dist/browser/creds/VerifiableCredential.js index 0ccaf573..ab16393c 100644 --- a/dist/browser/creds/VerifiableCredential.js +++ b/dist/browser/creds/VerifiableCredential.js @@ -1 +1 @@ -"use strict";var _slicedToArray2=require("babel-runtime/helpers/slicedToArray"),_slicedToArray3=_interopRequireDefault(_slicedToArray2),_regenerator=require("babel-runtime/regenerator"),_regenerator2=_interopRequireDefault(_regenerator),_asyncToGenerator2=require("babel-runtime/helpers/asyncToGenerator"),_asyncToGenerator3=_interopRequireDefault(_asyncToGenerator2),_classCallCheck2=require("babel-runtime/helpers/classCallCheck"),_classCallCheck3=_interopRequireDefault(_classCallCheck2),_createClass2=require("babel-runtime/helpers/createClass"),_createClass3=_interopRequireDefault(_createClass2);function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var _=require("lodash"),sift=require("sift"),MerkleTools=require("merkle-tools"),sjcl=require("sjcl"),timestamp=require("unix-timestamp"),flatten=require("flat"),uuidv4=require("uuid/v4"),definitions=require("./definitions"),UCA=require("../uca/UserCollectableAttribute"),SecureRandom=require("../SecureRandom"),_require=require("../services"),services=_require.services,anchorService=services.container.AnchorService;function sha256(a){return sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(a))}function getClaimPath(a,b){var c=_.split(a,":"),d=_.lowerCase(c[1]),e=d+"."+c[2],f=_.find(b,function(a){return _.endsWith(a,e)});return f||e}function validIdentifiers(){var a=_.map(definitions,function(a){return a.identifier});return a}function getClaimsWithFlatKeys(a){var b=flatten(a,{maxDepth:3}),c=flatten(a,{maxDepth:2}),d=_.merge({},b,c),e=_(d).toPairs().sortBy(0).fromPairs().value();return e}function getLeavesClaimPaths(a){return _.map(a,"claimPath")}function verifyLeave(a,b,c,d,e,f,g){var h=new UCA(a.identifier,{attestableValue:a.value});if("String"===h.type||"Number"===h.type)h.value!==_.get(c,a.claimPath)&&e.push(a.value);else if("Object"===h.type){var i=h.value,j=_.get(c,a.claimPath),k=_.keys(h.value);_.each(k,function(a){var b=_.get(i[a],"type"),c="Number"===b?_.padStart(j[a],8,"0"):j[a];c&&_.get(i[a],"value")!==c&&e.push(j[a])})}else e.push(a.value);var l=sha256(a.value);l!==a.targetHash&&f.push(a.targetHash);var m=b.validateProof(a.node,a.targetHash,d.merkleRoot);m||g.push(a.targetHash)}function transformConstraint(a){var b=[];return _.forEach(a.claims,function(a){if(!a.path)throw new Error("Malformed contraint: missing PATTH");if(!a.is)throw new Error("Malformed contraint: missing IS");var c={};c[a.path]=a.is,b.push(c)}),b}var CivicMerkleProof=function(){function a(b,c){(0,_classCallCheck3.default)(this,a);var d=a.padTree(b);this.type="CivicMerkleProof2018",this.merkleRoot=null,this.anchor="TBD (Civic Blockchain Attestation)",this.leaves=a.getAllAttestableValue(d),this.buildMerkleTree(c)}return(0,_createClass3.default)(a,null,[{key:"PADDING_INCREMENTS",get:function(){return 16}}]),(0,_createClass3.default)(a,[{key:"buildMerkleTree",value:function(a){var b=this,c=new MerkleTools,d=_.map(this.leaves,function(a){return sha256(a.value)});c.addLeaves(d),c.makeTree(),_.forEach(d,function(d,e){b.leaves[e].claimPath=getClaimPath(b.leaves[e].identifier,a),b.leaves[e].targetHash=d,b.leaves[e].node=c.getProof(e)}),this.leaves=_.filter(this.leaves,function(a){return"cvc:Random:node"!==a.identifier}),this.merkleRoot=c.getMerkleRoot().toString("hex")}}],[{key:"padTree",value:function(b){for(var c=b.length,d=ct.getTime()&&k.push(a)}}return _.isEmpty(j)&&_.isEmpty(l)&&_.isEmpty(m)&&_.isEmpty(n)&&_.isEmpty(k)&&(e=!0),e},this.verify=function(a){var b=a||VERIFY_LEVELS.PROOFS,c=VERIFY_LEVELS.INVALID;return b>=VERIFY_LEVELS.PROOFS&&f.verifyProofs()&&(c=VERIFY_LEVELS.PROOFS),c},this.verifySignature=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.verifySignature(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.verifyAttestation=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.verifyAttestation(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.revokeAttestation=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.revokeAttestation(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.isRevoked=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.isRevoked(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.isMatch=function(a){var b=transformConstraint(a),c=!0;return _.forEach(b,function(a){return c=-1t.getTime()&&k.push(a)}}return _.isEmpty(j)&&_.isEmpty(l)&&_.isEmpty(m)&&_.isEmpty(n)&&_.isEmpty(k)&&(e=!0),e},this.verify=function(a){var b=a||VERIFY_LEVELS.PROOFS,c=VERIFY_LEVELS.INVALID;return b>=VERIFY_LEVELS.PROOFS&&f.verifyProofs()&&(c=VERIFY_LEVELS.PROOFS),c},this.verifySignature=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.verifySignature(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.verifyAttestation=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.verifyAttestation(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.revokeAttestation=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.revokeAttestation(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.isRevoked=(0,_asyncToGenerator3.default)(_regenerator2.default.mark(function a(){return _regenerator2.default.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.abrupt("return",anchorService.isRevoked(f.proof));case 1:case"end":return a.stop();}},a,f)})),this.isMatch=function(a){var b=transformConstraint(a),c=!0;return _.forEach(b,function(a){return c=-1=c.minimumLength)&&(!c.maximumLength||a.length<=c.minimumLength):"Number"===b?((!_.isNil(c.minimum)&&c.exclusiveMinimum?a>c.minimum:a>=c.minimum)||_.isNil(c.minimum))&&((!_.isNil(c.maximum)&&c.exclusiveMaximum?a=c.minimumLength)&&(!c.maximumLength||a.length<=c.minimumLength):"Number"===b?((!_.isNil(c.minimum)&&c.exclusiveMinimum?a>c.minimum:a>=c.minimum)||_.isNil(c.minimum))&&((!_.isNil(c.maximum)&&c.exclusiveMaximum?a _.includes(definition.excludes, el.identifier)); _.forEach(removed, r => { @@ -342,7 +342,7 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, }; /** - * Verify the Credencial and return a verification level. + * Verify the Credential and return a verification level. * @return Any of VC.VERIFY_LEVELS */ this.verify = higherVerifyLevel => { @@ -384,7 +384,6 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, }); this.isMatch = constraints => { - const siftConstraints = transformConstraint(constraints); let result = true; @@ -474,6 +473,24 @@ VerifiableCredentialBaseConstructor.fromJSON = verifiableCredentialJSON => { return newObj; }; +/** + * List all properties al a Verifiable Credential + */ +VerifiableCredentialBaseConstructor.getAllProperties = identifier => { + const vcDefinition = _.find(definitions, { identifier }); + if (vcDefinition) { + const allProperties = []; + _.forEach(vcDefinition.depends, ucaIdentifier => { + allProperties.push(...UCA.getAllProperties(ucaIdentifier)); + }); + const excludesProperties = []; + _.forEach(vcDefinition.excludes, ucaIdentifier => { + excludesProperties.push(...UCA.getAllProperties(ucaIdentifier)); + }); + return _.difference(allProperties, excludesProperties); + } +}; + VerifiableCredentialBaseConstructor.VERIFY_LEVELS = VERIFY_LEVELS; module.exports = VerifiableCredentialBaseConstructor; \ No newline at end of file diff --git a/dist/cjs/creds/definitions.js b/dist/cjs/creds/definitions.js index 15c5963e..36997535 100644 --- a/dist/cjs/creds/definitions.js +++ b/dist/cjs/creds/definitions.js @@ -7,11 +7,11 @@ * @type {*[]} */ const definitions = [{ - identifier: 'cvc:Credential:email', + identifier: 'cvc:Credential:Email', version: '1', depends: ['cvc:Contact:email'] }, { - identifier: 'cvc:Credential:phoneNumber', + identifier: 'cvc:Credential:PhoneNumber', version: '1', depends: ['cvc:Contact:phoneNumber'] }, { diff --git a/dist/cjs/isClaimRelated.js b/dist/cjs/isClaimRelated.js index 089b8ac6..10d125ea 100644 --- a/dist/cjs/isClaimRelated.js +++ b/dist/cjs/isClaimRelated.js @@ -18,7 +18,7 @@ function isClaimRelated(claim, uca, credential) { const ucaDefinition = ucaDefinitions.find(definition => definition.identifier === ucaIdentifier); // does the UCA exist? if (ucaDefinition) { - const ucaProperties = UCA.getUCAProperties(ucaDefinition); + const ucaProperties = UCA.getAllProperties(ucaIdentifier); // does the claim exists in the UCA? if (_.includes(ucaProperties, claim)) { diff --git a/dist/cjs/services/httpService.js b/dist/cjs/services/httpService.js index 58fc86ca..12580b16 100644 --- a/dist/cjs/services/httpService.js +++ b/dist/cjs/services/httpService.js @@ -6,7 +6,7 @@ function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, a * A simple node HTTP services */ const request = require('request-promise-native'); -// uncloment to debug requests +// uncomment to debug requests // require('request-debug')(request); function HttpServiceConstructor() { diff --git a/dist/cjs/services/index.js b/dist/cjs/services/index.js index 38bea2e7..8ce0f7e5 100644 --- a/dist/cjs/services/index.js +++ b/dist/cjs/services/index.js @@ -9,26 +9,32 @@ const AnchorService = require('./anchorService'); const logger = require('../logger'); const HttpServiceConstructor = require('./httpService'); const config = require('./config'); +const SecureRandom = require('../SecureRandom'); const services = new Bottle(); /** * Init services with new values to config and http services - * @param {*} conf - * @param {*} http + * @param {*} conf + * @param {*} http + * @param secureRandom */ -const initServices = (conf, http) => { +const initServices = (conf, http, secureRandom) => { if (http) { services.resetProviders(['Http']); logger.debug('Registering custom HTTP service implementation'); services.factory('Http', () => http); } if (conf) { - services.resetProviders(['Http']); + services.resetProviders(['Config']); logger.debug('Registering custom Config service implementation'); services.factory('Config', () => conf); } - + if (secureRandom) { + services.resetProviders(['SecureRandom']); + logger.debug('Registering custom SecureRandom service implementation'); + services.factory('SecureRandom', () => secureRandom); + } return services; }; @@ -37,6 +43,8 @@ services.factory('Config', () => config); logger.info('Registering request-promise-native as Http service implementation.'); services.service('Http', HttpServiceConstructor); +services.service('SecureRandom', SecureRandom); + services.service('CivicAnchor', CurrentCivicAnchor, 'Config', 'Http'); services.factory('AnchorService', container => { diff --git a/dist/cjs/uca/UserCollectableAttribute.js b/dist/cjs/uca/UserCollectableAttribute.js index 53059d7b..6be287a2 100644 --- a/dist/cjs/uca/UserCollectableAttribute.js +++ b/dist/cjs/uca/UserCollectableAttribute.js @@ -3,8 +3,10 @@ const _ = require('lodash'); const timestamp = require('unix-timestamp'); const sjcl = require('sjcl'); -const SecureRandom = require('../SecureRandom'); const definitions = require('./definitions'); +const { services } = require('../services'); + +const secureRandom = services.container.SecureRandom; const validIdentifiers = _.map(definitions, d => d.identifier); @@ -69,7 +71,8 @@ const resolveType = definition => { return resolveType(refDefinition); }; -const getUCAProperties = (definition, pathName) => { +const getAllProperties = (identifier, pathName) => { + const definition = _.find(definitions, { identifier }); const properties = []; const type = resolveType(definition); const typeDefinition = _.isString(type) ? _.find(definitions, { identifier: type }) : definition; @@ -82,21 +85,37 @@ const getUCAProperties = (definition, pathName) => { const typeDefDefinition = _.find(definitions, { identifier: typeDefinition.type }); typeDefProps = resolveType(typeDefDefinition).properties; } - const basePropName = `${pathName ? `${pathName}.` : ''}${_.split(typeDefinition.identifier, ':')[2]}`; + + let basePropName; + const baseIdentifierComponents = _.split(typeDefinition.identifier, ':'); + if (pathName) { + if (_.includes(pathName, _.lowerCase(baseIdentifierComponents[1]))) { + basePropName = `${pathName}.${baseIdentifierComponents[2]}`; + } else { + basePropName = `${pathName}.${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + } + } else { + basePropName = `${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + } if (_.includes(['String', 'Number', 'Boolean'], `${typeDefProps.type}`)) { - // Propertie is not an object + // Properties is not an object properties.push(`${basePropName}.${typeDefProps.name}`); } else { _.forEach(typeDefProps, prop => { - const propDefinition = _.find(definitions, { identifier: prop.type }); - const proProperties = getUCAProperties(propDefinition, basePropName); + const typeSuffix = _.split(prop.type, ':')[2]; + const newBasePropName = prop.name === typeSuffix ? basePropName : `${basePropName}.${prop.name}`; + const proProperties = getAllProperties(prop.type, newBasePropName); _.forEach(proProperties, p => properties.push(p)); }); } } else if (pathName) { - const propertieName = `${pathName}.${_.split(definition.identifier, ':')[2]}`; - properties.push(propertieName); + const propertiesName = `${pathName}.${_.split(definition.identifier, ':')[2]}`; + properties.push(propertiesName); + } else { + const identifierComponents = _.split(identifier, ':'); + const propertiesName = `${_.lowerCase(identifierComponents[1])}.${identifierComponents[2]}`; + properties.push(propertiesName); } return properties; }; @@ -172,7 +191,8 @@ function UCABaseConstructor(identifier, value, version) { throw new Error(`${JSON.stringify(value)} is not valid for ${identifier}`); } this.value = value; - this.salt = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(SecureRandom.wordWith(64))); + + this.salt = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(secureRandom.wordWith(64))); } else if (_.isEmpty(definition.type.properties)) { throw new Error(`${JSON.stringify(value)} is not valid for ${identifier}`); } else { @@ -211,19 +231,19 @@ function UCABaseConstructor(identifier, value, version) { this.getGlobalCredentialItemIdentifier = () => `claim-${this.identifier}-${this.version}`; this.getClaimRootPropertyName = () => { - const identifierComponentes = _.split(this.identifier, ':'); - return _.lowerCase(identifierComponentes[1]); + const identifierComponents = _.split(this.identifier, ':'); + return _.lowerCase(identifierComponents[1]); }; this.getClaimPropertyName = () => { - const identifierComponentes = _.split(this.identifier, ':'); - return identifierComponentes[2]; + const identifierComponents = _.split(this.identifier, ':'); + return identifierComponents[2]; }; this.getClaimPath = () => { - const identifierComponentes = _.split(this.identifier, ':'); - const baseName = _.lowerCase(identifierComponentes[1]); - return `${baseName}.${identifierComponentes[2]}`; + const identifierComponents = _.split(this.identifier, ':'); + const baseName = _.lowerCase(identifierComponents[1]); + return `${baseName}.${identifierComponents[2]}`; }; this.getAttestableValues = () => { @@ -282,9 +302,9 @@ function UCABaseConstructor(identifier, value, version) { const UCA = UCABaseConstructor; function convertIdentifierToClassName(identifier) { - const identifierComponentes = _.split(identifier, ':'); - const baseName = identifierComponentes[1]; - const detailName = _.upperFirst(_.camelCase(identifierComponentes[2])); + const identifierComponents = _.split(identifier, ':'); + const baseName = identifierComponents[1]; + const detailName = _.upperFirst(_.camelCase(identifierComponents[2])); return `${baseName}${detailName}`; } @@ -304,6 +324,6 @@ _.forEach(_.filter(definitions, d => d.credentialItem), def => { UCA.getTypeName = getTypeName; UCA.resolveType = resolveType; -UCA.getUCAProperties = getUCAProperties; +UCA.getAllProperties = getAllProperties; module.exports = UCA; \ No newline at end of file diff --git a/dist/cjs/uca/definitions.js b/dist/cjs/uca/definitions.js index 80bdaa8e..a91642c8 100644 --- a/dist/cjs/uca/definitions.js +++ b/dist/cjs/uca/definitions.js @@ -28,18 +28,19 @@ const definitions = [{ type: 'String', attestable: true }, { - identifier: 'cvc:Domain:local_part', - description: 'also known as email domian', + identifier: 'cvc:Domain:name', + description: 'also known as email address domain', version: '1', type: 'String', credentialItem: false }, { identifier: 'cvc:Domain:tld', + description: 'also known as email address domain suffix, like .com, .org, .com.br', version: '1', type: 'String', credentialItem: false }, { - identifier: 'cvc:Email:address', + identifier: 'cvc:Email:username', description: 'also known as email user', version: '1', type: 'String', @@ -52,10 +53,10 @@ const definitions = [{ name: 'tld', type: 'cvc:Domain:tld' }, { - name: 'local_part', - type: 'cvc:Domain:local_part' + name: 'name', + type: 'cvc:Domain:name' }], - required: ['local_part', 'tld'] + required: ['name', 'tld'] }, credentialItem: false }, { @@ -68,11 +69,11 @@ const definitions = [{ version: '1', type: { properties: [{ + name: 'username', + type: 'cvc:Email:username' + }, { name: 'domain', type: 'cvc:Email:domain' - }, { - name: 'address', - type: 'cvc:Email:address' }] }, credentialItem: true @@ -243,7 +244,8 @@ const definitions = [{ }, { identifier: 'cvc:Address:street', version: '1', - type: 'String' + type: 'String', + credentialItem: true }, { identifier: 'cvc:Address:unit', version: '1', diff --git a/dist/es/SecureRandom.js b/dist/es/SecureRandom.js index 577377da..7d560a04 100644 --- a/dist/es/SecureRandom.js +++ b/dist/es/SecureRandom.js @@ -1,29 +1,35 @@ const sjcl = require('sjcl'); const logger = require('./logger'); -class SecureRandomizer { - constructor() { - logger.debug('Init Secure Randon'); - // eslint-disable-next-line +class SecureRandom { + constructor(seedHexString) { + logger.debug('Init Secure Random'); + // eslint-disable-next-line new-cap this.sjclRandom = new sjcl.prng(10); - try { - logger.debug('Trying crypto'); - /* eslint-disable global-require */ - const hexString = require('crypto').randomBytes(1024).toString('hex'); - /* eslint-enable global-require */ - const seed = sjcl.codec.hex.toBits(hexString); + if (seedHexString) { + const seed = sjcl.codec.hex.toBits(seedHexString); this.sjclRandom.addEntropy(seed, undefined, 'csprng'); this.isSeeded = true; - } catch (error) { - logger.warn(`Crypto: ${error}`); - this.isSeeded = false; + } else { + try { + logger.debug('Trying crypto'); + /* eslint-disable global-require */ + const hexString = require('crypto').randomBytes(1024).toString('hex'); + /* eslint-enable global-require */ + const seed = sjcl.codec.hex.toBits(hexString); + this.sjclRandom.addEntropy(seed, undefined, 'csprng'); + this.isSeeded = true; + } catch (error) { + logger.warn(`Crypto: ${error}`); + this.isSeeded = false; + } } } wordWith(size) { if (!this.isSeeded) { - throw new Error("Can't user SecureRandon before seeding"); + throw new Error("Can't user SecureRandom before seeding"); } const randomBytes = this.sjclRandom.randomWords(size / 8, 10); @@ -31,4 +37,4 @@ class SecureRandomizer { } } -module.exports = new SecureRandomizer(); \ No newline at end of file +module.exports = SecureRandom; \ No newline at end of file diff --git a/dist/es/creds/VerifiableCredential.js b/dist/es/creds/VerifiableCredential.js index f283757e..08a53349 100644 --- a/dist/es/creds/VerifiableCredential.js +++ b/dist/es/creds/VerifiableCredential.js @@ -7,10 +7,10 @@ const flatten = require('flat'); const uuidv4 = require('uuid/v4'); const definitions = require('./definitions'); const UCA = require('../uca/UserCollectableAttribute'); -const SecureRandom = require('../SecureRandom'); const { services } = require('../services'); const anchorService = services.container.AnchorService; +const secureRandom = services.container.SecureRandom; function sha256(string) { return sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(string)); @@ -99,19 +99,19 @@ function transformConstraint(constraints) { } /** - * Transforms a list of UCAs into the signature property of the verifiable cliams + * Transforms a list of UCAs into the signature property of the verifiable claims */ -class CivicMerkleProof { +class CvcMerkleProof { static get PADDING_INCREMENTS() { return 16; } constructor(ucas, claimsPathRef) { - const withRandomUcas = CivicMerkleProof.padTree(ucas); - this.type = 'CivicMerkleProof2018'; + const withRandomUcas = CvcMerkleProof.padTree(ucas); + this.type = 'CvcMerkleProof2018'; this.merkleRoot = null; this.anchor = 'TBD (Civic Blockchain Attestation)'; - this.leaves = CivicMerkleProof.getAllAttestableValue(withRandomUcas); + this.leaves = CvcMerkleProof.getAllAttestableValue(withRandomUcas); this.buildMerkleTree(claimsPathRef); } @@ -131,10 +131,10 @@ class CivicMerkleProof { static padTree(nodes) { const currentLength = nodes.length; - const targetLength = currentLength < CivicMerkleProof.PADDING_INCREMENTS ? CivicMerkleProof.PADDING_INCREMENTS : _.ceil(currentLength / CivicMerkleProof.PADDING_INCREMENTS) * CivicMerkleProof.PADDING_INCREMENTS; + const targetLength = currentLength < CvcMerkleProof.PADDING_INCREMENTS ? CvcMerkleProof.PADDING_INCREMENTS : _.ceil(currentLength / CvcMerkleProof.PADDING_INCREMENTS) * CvcMerkleProof.PADDING_INCREMENTS; const newNodes = _.clone(nodes); while (newNodes.length < targetLength) { - newNodes.push(new UCA('cvc:Random:node', SecureRandom.wordWith(16))); + newNodes.push(new UCA('cvc:Random:node', secureRandom.wordWith(16))); } return newNodes; } @@ -207,7 +207,7 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, if (!_.isEmpty(ucas)) { this.claim = new ClaimModel(ucas); const claimsPathRef = _.keys(flatten(this.claim, { safe: true })); - this.proof = new CivicMerkleProof(proofUCAs, claimsPathRef); + this.proof = new CvcMerkleProof(proofUCAs, claimsPathRef); if (!_.isEmpty(definition.excludes)) { const removed = _.remove(this.proof.leaves, el => _.includes(definition.excludes, el.identifier)); _.forEach(removed, r => { @@ -330,7 +330,7 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, }; /** - * Verify the Credencial and return a verification level. + * Verify the Credential and return a verification level. * @return Any of VC.VERIFY_LEVELS */ this.verify = higherVerifyLevel => { @@ -364,7 +364,6 @@ function VerifiableCredentialBaseConstructor(identifier, issuer, expiryIn, ucas, this.isRevoked = async () => anchorService.isRevoked(this.proof); this.isMatch = constraints => { - const siftConstraints = transformConstraint(constraints); let result = true; @@ -454,6 +453,24 @@ VerifiableCredentialBaseConstructor.fromJSON = verifiableCredentialJSON => { return newObj; }; +/** + * List all properties al a Verifiable Credential + */ +VerifiableCredentialBaseConstructor.getAllProperties = identifier => { + const vcDefinition = _.find(definitions, { identifier }); + if (vcDefinition) { + const allProperties = []; + _.forEach(vcDefinition.depends, ucaIdentifier => { + allProperties.push(...UCA.getAllProperties(ucaIdentifier)); + }); + const excludesProperties = []; + _.forEach(vcDefinition.excludes, ucaIdentifier => { + excludesProperties.push(...UCA.getAllProperties(ucaIdentifier)); + }); + return _.difference(allProperties, excludesProperties); + } +}; + VerifiableCredentialBaseConstructor.VERIFY_LEVELS = VERIFY_LEVELS; module.exports = VerifiableCredentialBaseConstructor; \ No newline at end of file diff --git a/dist/es/creds/definitions.js b/dist/es/creds/definitions.js index 56f66cfc..d317db35 100644 --- a/dist/es/creds/definitions.js +++ b/dist/es/creds/definitions.js @@ -5,11 +5,11 @@ * @type {*[]} */ const definitions = [{ - identifier: 'cvc:Credential:email', + identifier: 'cvc:Credential:Email', version: '1', depends: ['cvc:Contact:email'] }, { - identifier: 'cvc:Credential:phoneNumber', + identifier: 'cvc:Credential:PhoneNumber', version: '1', depends: ['cvc:Contact:phoneNumber'] }, { diff --git a/dist/es/isClaimRelated.js b/dist/es/isClaimRelated.js index 318229b8..a3b6e97f 100644 --- a/dist/es/isClaimRelated.js +++ b/dist/es/isClaimRelated.js @@ -16,7 +16,7 @@ function isClaimRelated(claim, uca, credential) { const ucaDefinition = ucaDefinitions.find(definition => definition.identifier === ucaIdentifier); // does the UCA exist? if (ucaDefinition) { - const ucaProperties = UCA.getUCAProperties(ucaDefinition); + const ucaProperties = UCA.getAllProperties(ucaIdentifier); // does the claim exists in the UCA? if (_.includes(ucaProperties, claim)) { diff --git a/dist/es/services/httpService.js b/dist/es/services/httpService.js index aabaa269..b190f00d 100644 --- a/dist/es/services/httpService.js +++ b/dist/es/services/httpService.js @@ -2,7 +2,7 @@ * A simple node HTTP services */ const request = require('request-promise-native'); -// uncloment to debug requests +// uncomment to debug requests // require('request-debug')(request); function HttpServiceConstructor() { diff --git a/dist/es/services/index.js b/dist/es/services/index.js index 0e96547b..0d9f15e4 100644 --- a/dist/es/services/index.js +++ b/dist/es/services/index.js @@ -7,26 +7,32 @@ const AnchorService = require('./anchorService'); const logger = require('../logger'); const HttpServiceConstructor = require('./httpService'); const config = require('./config'); +const SecureRandom = require('../SecureRandom'); const services = new Bottle(); /** * Init services with new values to config and http services - * @param {*} conf - * @param {*} http + * @param {*} conf + * @param {*} http + * @param secureRandom */ -const initServices = (conf, http) => { +const initServices = (conf, http, secureRandom) => { if (http) { services.resetProviders(['Http']); logger.debug('Registering custom HTTP service implementation'); services.factory('Http', () => http); } if (conf) { - services.resetProviders(['Http']); + services.resetProviders(['Config']); logger.debug('Registering custom Config service implementation'); services.factory('Config', () => conf); } - + if (secureRandom) { + services.resetProviders(['SecureRandom']); + logger.debug('Registering custom SecureRandom service implementation'); + services.factory('SecureRandom', () => secureRandom); + } return services; }; @@ -35,6 +41,8 @@ services.factory('Config', () => config); logger.info('Registering request-promise-native as Http service implementation.'); services.service('Http', HttpServiceConstructor); +services.service('SecureRandom', SecureRandom); + services.service('CivicAnchor', CurrentCivicAnchor, 'Config', 'Http'); services.factory('AnchorService', container => { diff --git a/dist/es/uca/UserCollectableAttribute.js b/dist/es/uca/UserCollectableAttribute.js index e78bd9f8..acba8b02 100644 --- a/dist/es/uca/UserCollectableAttribute.js +++ b/dist/es/uca/UserCollectableAttribute.js @@ -1,8 +1,10 @@ const _ = require('lodash'); const timestamp = require('unix-timestamp'); const sjcl = require('sjcl'); -const SecureRandom = require('../SecureRandom'); const definitions = require('./definitions'); +const { services } = require('../services'); + +const secureRandom = services.container.SecureRandom; const validIdentifiers = _.map(definitions, d => d.identifier); @@ -67,7 +69,8 @@ const resolveType = definition => { return resolveType(refDefinition); }; -const getUCAProperties = (definition, pathName) => { +const getAllProperties = (identifier, pathName) => { + const definition = _.find(definitions, { identifier }); const properties = []; const type = resolveType(definition); const typeDefinition = _.isString(type) ? _.find(definitions, { identifier: type }) : definition; @@ -80,21 +83,37 @@ const getUCAProperties = (definition, pathName) => { const typeDefDefinition = _.find(definitions, { identifier: typeDefinition.type }); typeDefProps = resolveType(typeDefDefinition).properties; } - const basePropName = `${pathName ? `${pathName}.` : ''}${_.split(typeDefinition.identifier, ':')[2]}`; + + let basePropName; + const baseIdentifierComponents = _.split(typeDefinition.identifier, ':'); + if (pathName) { + if (_.includes(pathName, _.lowerCase(baseIdentifierComponents[1]))) { + basePropName = `${pathName}.${baseIdentifierComponents[2]}`; + } else { + basePropName = `${pathName}.${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + } + } else { + basePropName = `${_.lowerCase(baseIdentifierComponents[1])}.${baseIdentifierComponents[2]}`; + } if (_.includes(['String', 'Number', 'Boolean'], `${typeDefProps.type}`)) { - // Propertie is not an object + // Properties is not an object properties.push(`${basePropName}.${typeDefProps.name}`); } else { _.forEach(typeDefProps, prop => { - const propDefinition = _.find(definitions, { identifier: prop.type }); - const proProperties = getUCAProperties(propDefinition, basePropName); + const typeSuffix = _.split(prop.type, ':')[2]; + const newBasePropName = prop.name === typeSuffix ? basePropName : `${basePropName}.${prop.name}`; + const proProperties = getAllProperties(prop.type, newBasePropName); _.forEach(proProperties, p => properties.push(p)); }); } } else if (pathName) { - const propertieName = `${pathName}.${_.split(definition.identifier, ':')[2]}`; - properties.push(propertieName); + const propertiesName = `${pathName}.${_.split(definition.identifier, ':')[2]}`; + properties.push(propertiesName); + } else { + const identifierComponents = _.split(identifier, ':'); + const propertiesName = `${_.lowerCase(identifierComponents[1])}.${identifierComponents[2]}`; + properties.push(propertiesName); } return properties; }; @@ -170,7 +189,8 @@ function UCABaseConstructor(identifier, value, version) { throw new Error(`${JSON.stringify(value)} is not valid for ${identifier}`); } this.value = value; - this.salt = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(SecureRandom.wordWith(64))); + + this.salt = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(secureRandom.wordWith(64))); } else if (_.isEmpty(definition.type.properties)) { throw new Error(`${JSON.stringify(value)} is not valid for ${identifier}`); } else { @@ -209,19 +229,19 @@ function UCABaseConstructor(identifier, value, version) { this.getGlobalCredentialItemIdentifier = () => `claim-${this.identifier}-${this.version}`; this.getClaimRootPropertyName = () => { - const identifierComponentes = _.split(this.identifier, ':'); - return _.lowerCase(identifierComponentes[1]); + const identifierComponents = _.split(this.identifier, ':'); + return _.lowerCase(identifierComponents[1]); }; this.getClaimPropertyName = () => { - const identifierComponentes = _.split(this.identifier, ':'); - return identifierComponentes[2]; + const identifierComponents = _.split(this.identifier, ':'); + return identifierComponents[2]; }; this.getClaimPath = () => { - const identifierComponentes = _.split(this.identifier, ':'); - const baseName = _.lowerCase(identifierComponentes[1]); - return `${baseName}.${identifierComponentes[2]}`; + const identifierComponents = _.split(this.identifier, ':'); + const baseName = _.lowerCase(identifierComponents[1]); + return `${baseName}.${identifierComponents[2]}`; }; this.getAttestableValues = () => { @@ -280,9 +300,9 @@ function UCABaseConstructor(identifier, value, version) { const UCA = UCABaseConstructor; function convertIdentifierToClassName(identifier) { - const identifierComponentes = _.split(identifier, ':'); - const baseName = identifierComponentes[1]; - const detailName = _.upperFirst(_.camelCase(identifierComponentes[2])); + const identifierComponents = _.split(identifier, ':'); + const baseName = identifierComponents[1]; + const detailName = _.upperFirst(_.camelCase(identifierComponents[2])); return `${baseName}${detailName}`; } @@ -302,6 +322,6 @@ _.forEach(_.filter(definitions, d => d.credentialItem), def => { UCA.getTypeName = getTypeName; UCA.resolveType = resolveType; -UCA.getUCAProperties = getUCAProperties; +UCA.getAllProperties = getAllProperties; module.exports = UCA; \ No newline at end of file diff --git a/dist/es/uca/definitions.js b/dist/es/uca/definitions.js index 58931390..3cacd2aa 100644 --- a/dist/es/uca/definitions.js +++ b/dist/es/uca/definitions.js @@ -26,18 +26,19 @@ const definitions = [{ type: 'String', attestable: true }, { - identifier: 'cvc:Domain:local_part', - description: 'also known as email domian', + identifier: 'cvc:Domain:name', + description: 'also known as email address domain', version: '1', type: 'String', credentialItem: false }, { identifier: 'cvc:Domain:tld', + description: 'also known as email address domain suffix, like .com, .org, .com.br', version: '1', type: 'String', credentialItem: false }, { - identifier: 'cvc:Email:address', + identifier: 'cvc:Email:username', description: 'also known as email user', version: '1', type: 'String', @@ -50,10 +51,10 @@ const definitions = [{ name: 'tld', type: 'cvc:Domain:tld' }, { - name: 'local_part', - type: 'cvc:Domain:local_part' + name: 'name', + type: 'cvc:Domain:name' }], - required: ['local_part', 'tld'] + required: ['name', 'tld'] }, credentialItem: false }, { @@ -66,11 +67,11 @@ const definitions = [{ version: '1', type: { properties: [{ + name: 'username', + type: 'cvc:Email:username' + }, { name: 'domain', type: 'cvc:Email:domain' - }, { - name: 'address', - type: 'cvc:Email:address' }] }, credentialItem: true @@ -241,7 +242,8 @@ const definitions = [{ }, { identifier: 'cvc:Address:street', version: '1', - type: 'String' + type: 'String', + credentialItem: true }, { identifier: 'cvc:Address:unit', version: '1', diff --git a/package.json b/package.json index 9de7079f..793f47af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@identity.com/credential-commons", - "version": "0.2.23", + "version": "0.2.24", "author": "Identity.com Community", "license": "MIT", "description": "Verifiable Credential and Attestation Library",