commit
						7480151755
					
				 11 changed files with 1512 additions and 0 deletions
			
			
		@ -0,0 +1,2 @@ | 
				
			|||
# Set the default behavior, in case people don't have core.autocrlf set. | 
				
			|||
* text=auto eol=lf | 
				
			|||
@ -0,0 +1,15 @@ | 
				
			|||
# Node | 
				
			|||
/node_modules | 
				
			|||
 | 
				
			|||
# IDE | 
				
			|||
.DS_Store | 
				
			|||
.idea | 
				
			|||
 | 
				
			|||
# Logs | 
				
			|||
/log | 
				
			|||
npm-debug.log* | 
				
			|||
yarn-debug.log* | 
				
			|||
yarn-error.log* | 
				
			|||
 | 
				
			|||
# npm | 
				
			|||
*.tgz | 
				
			|||
@ -0,0 +1,9 @@ | 
				
			|||
# Jetbrains | 
				
			|||
.idea | 
				
			|||
 | 
				
			|||
# Git | 
				
			|||
.gitattributes | 
				
			|||
 | 
				
			|||
# Package managers | 
				
			|||
yarn.lock | 
				
			|||
*.tgz | 
				
			|||
@ -0,0 +1,21 @@ | 
				
			|||
MIT License | 
				
			|||
 | 
				
			|||
Copyright (c) 2020 Ezerous | 
				
			|||
 | 
				
			|||
Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
of this software and associated documentation files (the "Software"), to deal | 
				
			|||
in the Software without restriction, including without limitation the rights | 
				
			|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
copies of the Software, and to permit persons to whom the Software is | 
				
			|||
furnished to do so, subject to the following conditions: | 
				
			|||
 | 
				
			|||
The above copyright notice and this permission notice shall be included in all | 
				
			|||
copies or substantial portions of the Software. | 
				
			|||
 | 
				
			|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
SOFTWARE. | 
				
			|||
@ -0,0 +1,7 @@ | 
				
			|||
# @ezerous/eth-identity-provider | 
				
			|||
 | 
				
			|||
 [](https://www.npmjs.com/package/@ezerous/eth-identity-provider) | 
				
			|||
 [](https://david-dm.org/Ezerous/eth-identity-provider) | 
				
			|||
 [](https://www.npmjs.com/package/@ezerous/eth-identity-provider) | 
				
			|||
  | 
				
			|||
  An Ethereum [OrbitDB identity provider](https://github.com/orbitdb/orbit-db-identity-provider). | 
				
			|||
@ -0,0 +1,13 @@ | 
				
			|||
{ | 
				
			|||
  "name": "@ezerous/eth-identity-provider", | 
				
			|||
  "version": "0.1.0", | 
				
			|||
  "description": "An Ethereum orbit-db-identity-provider.", | 
				
			|||
  "license": "MIT", | 
				
			|||
  "author": "Ezerous <ezerous@gmail.com>", | 
				
			|||
  "main": "src/index.js", | 
				
			|||
  "repository": "github:Ezerous/breeze", | 
				
			|||
  "dependencies": { | 
				
			|||
    "level": "~6.0.1", | 
				
			|||
    "orbit-db-identity-provider": "~0.3.1" | 
				
			|||
  } | 
				
			|||
} | 
				
			|||
@ -0,0 +1,4 @@ | 
				
			|||
import EthereumIdentityProvider from "./ΕthereumIdentityProvider"; | 
				
			|||
import EthereumContractIdentityProvider from "./ΕthereumContractIdentityProvider"; | 
				
			|||
 | 
				
			|||
export { EthereumIdentityProvider, EthereumContractIdentityProvider }; | 
				
			|||
@ -0,0 +1,23 @@ | 
				
			|||
import level from 'level'; | 
				
			|||
 | 
				
			|||
/* Used in development only to store the identity.signatures.publicKey so developers don't have to | 
				
			|||
repeatedly sign theOrbitDB creation transaction in MetaMask when React development server reloads | 
				
			|||
the app */ | 
				
			|||
const concordiaDB = level('./concordia/identities'); | 
				
			|||
 | 
				
			|||
async function storeIdentitySignaturePubKey(key, signaturePubKey) { | 
				
			|||
  await concordiaDB.put(key, signaturePubKey); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// If it exists, it returns the identity.signatures.publicKey for the given key (key is the
 | 
				
			|||
// concatenation of identity.publicKey + identity.signatures.id)
 | 
				
			|||
async function getIdentitySignaturePubKey(key) { | 
				
			|||
  try { | 
				
			|||
    return await concordiaDB.get(key); | 
				
			|||
  } catch (err) { | 
				
			|||
    if (err && err.notFound) return null; // Not found
 | 
				
			|||
    throw err; | 
				
			|||
  } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export { storeIdentitySignaturePubKey, getIdentitySignaturePubKey }; | 
				
			|||
@ -0,0 +1,131 @@ | 
				
			|||
/* eslint-disable no-console */ | 
				
			|||
/* eslint-disable no-return-await */ | 
				
			|||
import IdentityProvider from 'orbit-db-identity-provider'; | 
				
			|||
import { getIdentitySignaturePubKey, storeIdentitySignaturePubKey } from './levelUtils'; | 
				
			|||
 | 
				
			|||
const LOGGING_PREFIX = 'EthereumContractIdentityProvider: '; | 
				
			|||
 | 
				
			|||
class EthereumContractIdentityProvider extends IdentityProvider { | 
				
			|||
  constructor(options = {}) { | 
				
			|||
    if (!EthereumContractIdentityProvider.web3) { | 
				
			|||
      throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because web3 wasn't set. ` | 
				
			|||
          + 'Please use setWeb3(web3) first!'); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    if (!EthereumContractIdentityProvider.contractAddress) { | 
				
			|||
      throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because contractAddress wasn't set. ` | 
				
			|||
          + 'Please use setContractAddress(contractAddress) first!'); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    super(options); | 
				
			|||
 | 
				
			|||
    // Optional (will be grabbed later if omitted)
 | 
				
			|||
    const { id } = options; | 
				
			|||
    if (id) { | 
				
			|||
      //  Set Orbit's Identity Id (user's Ethereum address + contract's address)
 | 
				
			|||
      const { userAddress, contractAddress } = EthereumContractIdentityProvider.splitId(id); | 
				
			|||
      if (EthereumContractIdentityProvider.web3.utils.isAddress(userAddress) | 
				
			|||
          && EthereumContractIdentityProvider.contractAddress === contractAddress) { | 
				
			|||
        this.id = id; | 
				
			|||
        this.userAddress = userAddress; | 
				
			|||
      } | 
				
			|||
      else | 
				
			|||
        throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because an invalid id was supplied.`); | 
				
			|||
    } | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  static get type() { return 'ethereum'; } | 
				
			|||
 | 
				
			|||
  async getId() { | 
				
			|||
    // Id wasn't in the constructor, grab it now
 | 
				
			|||
    if (!this.id) { | 
				
			|||
      const accounts = await EthereumContractIdentityProvider.web3.eth.getAccounts(); | 
				
			|||
      if (!accounts[0]) { | 
				
			|||
        throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because no web3 accounts were found (
 | 
				
			|||
                locked Metamask?).`);
 | 
				
			|||
      } | 
				
			|||
      [this.userAddress] = accounts; | 
				
			|||
      this.id = this.userAddress + EthereumContractIdentityProvider.contractAddress; | 
				
			|||
    } | 
				
			|||
    return this.id; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  async signIdentity(data) { | 
				
			|||
    if (process.env.NODE_ENV === 'development') { // Don't sign repeatedly while in development
 | 
				
			|||
      console.debug(`${LOGGING_PREFIX}Attempting to find stored Orbit identity data...`); | 
				
			|||
      const signaturePubKey = await getIdentitySignaturePubKey(data); | 
				
			|||
      if (signaturePubKey) { | 
				
			|||
        const identity = { | 
				
			|||
          userAddress: this.userAddress, | 
				
			|||
          pubKeySignId: data, | 
				
			|||
          signatures: { publicKey: signaturePubKey }, | 
				
			|||
        }; | 
				
			|||
        if (await EthereumContractIdentityProvider.verifyIdentity(identity)) { | 
				
			|||
          console.debug(`${LOGGING_PREFIX}Found and verified stored Orbit identity data!`); | 
				
			|||
          return signaturePubKey; | 
				
			|||
        } | 
				
			|||
        console.debug(`${LOGGING_PREFIX}Stored Orbit identity data couldn't be verified.`); | 
				
			|||
      } else console.debug(`${LOGGING_PREFIX}No stored Orbit identity data were found.`); | 
				
			|||
    } | 
				
			|||
    return await this.doSignIdentity(data); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // eslint-disable-next-line consistent-return
 | 
				
			|||
  async doSignIdentity(data) { | 
				
			|||
    try { | 
				
			|||
      const signaturePubKey = await EthereumContractIdentityProvider.web3.eth.personal.sign(data, this.userAddress, ''); | 
				
			|||
      if (process.env.NODE_ENV === 'development') { | 
				
			|||
        storeIdentitySignaturePubKey(data, signaturePubKey) | 
				
			|||
            .then(() => { | 
				
			|||
              console.debug(`${LOGGING_PREFIX}Successfully stored current Orbit identity data.`); | 
				
			|||
            }) | 
				
			|||
            .catch(() => { | 
				
			|||
              console.warn(`${LOGGING_PREFIX}Couldn't store current Orbit identity data...`); | 
				
			|||
            }); | 
				
			|||
      } | 
				
			|||
      return signaturePubKey; // Password not required for MetaMask
 | 
				
			|||
    } catch (error) { | 
				
			|||
      if (error.code && error.code === 4001) { | 
				
			|||
        console.debug(`${LOGGING_PREFIX}User denied message signature.`); | 
				
			|||
        return await this.doSignIdentity(data); | 
				
			|||
      } | 
				
			|||
 | 
				
			|||
      console.error(`${LOGGING_PREFIX}Failed to sign data.`); | 
				
			|||
      console.error(error); | 
				
			|||
    } | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  static async verifyIdentity(identity) { | 
				
			|||
    const pubKeySignId = identity.pubKeySignId ? identity.pubKeySignId : identity.publicKey + identity.signatures.id; | 
				
			|||
    const { userAddress } = identity.userAddress ? identity: EthereumContractIdentityProvider.splitId(identity.id); | 
				
			|||
 | 
				
			|||
    // Verify that identity was signed by the ID
 | 
				
			|||
    return new Promise((resolve) => { | 
				
			|||
      resolve(EthereumContractIdentityProvider.web3.eth.accounts.recover(pubKeySignId, | 
				
			|||
          identity.signatures.publicKey) === userAddress); | 
				
			|||
    }); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // Initialize by supplying a web3 object
 | 
				
			|||
  static setWeb3(web3) { | 
				
			|||
    EthereumContractIdentityProvider.web3 = web3; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // Initialize by supplying a contract's address (to be used as a point of reference)
 | 
				
			|||
  static setContractAddress(contractAddress) { | 
				
			|||
    EthereumContractIdentityProvider.contractAddress = contractAddress; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  static splitId(id) { | 
				
			|||
    const regex = /(0x.*)(0x.*)/g; | 
				
			|||
    const match = regex.exec(id); | 
				
			|||
    if (match && match.length === 3) | 
				
			|||
      return { userAddress: match[1], contractAddress: match[2] }; | 
				
			|||
    throw new Error(`${LOGGING_PREFIX}Invalid id ${id}! Couldn't split it to userAddress, contractAddress.`); | 
				
			|||
  } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
EthereumContractIdentityProvider.web3 = {}; | 
				
			|||
EthereumContractIdentityProvider.contractAddress = {}; | 
				
			|||
 | 
				
			|||
export default EthereumContractIdentityProvider; | 
				
			|||
@ -0,0 +1,107 @@ | 
				
			|||
/* eslint-disable no-console */ | 
				
			|||
/* eslint-disable no-return-await */ | 
				
			|||
import IdentityProvider from 'orbit-db-identity-provider'; | 
				
			|||
import { getIdentitySignaturePubKey, storeIdentitySignaturePubKey } from './levelUtils'; | 
				
			|||
 | 
				
			|||
const LOGGING_PREFIX = 'EthereumIdentityProvider: '; | 
				
			|||
 | 
				
			|||
class EthereumIdentityProvider extends IdentityProvider { | 
				
			|||
  constructor(options = {}) { | 
				
			|||
    if (!EthereumIdentityProvider.web3) { | 
				
			|||
      throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because web3 wasn't set. ` | 
				
			|||
          + 'Please use setWeb3(web3) first!'); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    super(options); | 
				
			|||
 | 
				
			|||
    // Optional (will be grabbed later if omitted)
 | 
				
			|||
    const { id } = options; | 
				
			|||
    if (id){ | 
				
			|||
        //  Set Orbit's Identity Id (user's Ethereum address)
 | 
				
			|||
        if (EthereumIdentityProvider.web3.utils.isAddress(id)) | 
				
			|||
          this.id = id; | 
				
			|||
        else | 
				
			|||
          throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because an invalid id was supplied.`); | 
				
			|||
    } | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  static get type() { return 'ethereum'; } | 
				
			|||
 | 
				
			|||
  async getId() { | 
				
			|||
    // Id wasn't in the constructor, grab it now
 | 
				
			|||
    if (!this.id) { | 
				
			|||
      const accounts = await EthereumIdentityProvider.web3.eth.getAccounts(); | 
				
			|||
      if (!accounts[0]) { | 
				
			|||
        throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because no web3 accounts were found (
 | 
				
			|||
                locked Metamask?).`);
 | 
				
			|||
      } | 
				
			|||
 | 
				
			|||
      [this.id] = accounts; | 
				
			|||
    } | 
				
			|||
    return this.id; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // Data to be signed is identity.publicKey + identity.signatures.id
 | 
				
			|||
  async signIdentity(data) { | 
				
			|||
    if (process.env.NODE_ENV === 'development') { // Don't sign repeatedly while in development
 | 
				
			|||
      console.debug(`${LOGGING_PREFIX}Attempting to find stored Orbit identity data...`); | 
				
			|||
      const signaturePubKey = await getIdentitySignaturePubKey(data); | 
				
			|||
      if (signaturePubKey) { | 
				
			|||
        const identity = { | 
				
			|||
          id: this.id, | 
				
			|||
          pubKeySignId: data, | 
				
			|||
          signatures: { publicKey: signaturePubKey }, | 
				
			|||
        }; | 
				
			|||
        if (await EthereumIdentityProvider.verifyIdentity(identity)) { | 
				
			|||
          console.debug(`${LOGGING_PREFIX}Found and verified stored Orbit identity data!`); | 
				
			|||
          return signaturePubKey; | 
				
			|||
        } | 
				
			|||
        console.debug(`${LOGGING_PREFIX}Stored Orbit identity data couldn't be verified.`); | 
				
			|||
      } else console.debug(`${LOGGING_PREFIX}No stored Orbit identity data were found.`); | 
				
			|||
    } | 
				
			|||
    return await this.doSignIdentity(data); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // eslint-disable-next-line consistent-return
 | 
				
			|||
  async doSignIdentity(data) { | 
				
			|||
    try { | 
				
			|||
      const signaturePubKey = await EthereumIdentityProvider.web3.eth.personal.sign(data, this.id, ''); | 
				
			|||
      if (process.env.NODE_ENV === 'development') { | 
				
			|||
        storeIdentitySignaturePubKey(data, signaturePubKey) | 
				
			|||
            .then(() => { | 
				
			|||
              console.debug(`${LOGGING_PREFIX}Successfully stored current Orbit identity data.`); | 
				
			|||
            }) | 
				
			|||
            .catch(() => { | 
				
			|||
              console.warn(`${LOGGING_PREFIX}Couldn't store current Orbit identity data...`); | 
				
			|||
            }); | 
				
			|||
      } | 
				
			|||
      return signaturePubKey; // Password not required for MetaMask
 | 
				
			|||
    } catch (error) { | 
				
			|||
      if (error.code && error.code === 4001) { | 
				
			|||
        console.debug(`${LOGGING_PREFIX}User denied message signature.`); | 
				
			|||
        return await this.doSignIdentity(data); | 
				
			|||
      } | 
				
			|||
 | 
				
			|||
      console.error(`${LOGGING_PREFIX}Failed to sign data.`); | 
				
			|||
      console.error(error); | 
				
			|||
    } | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // Verifies that identity was signed by the ID
 | 
				
			|||
  static async verifyIdentity(identity) { | 
				
			|||
    const pubKeySignId = identity.pubKeySignId ? identity.pubKeySignId : identity.publicKey + identity.signatures.id; | 
				
			|||
    return new Promise((resolve) => { | 
				
			|||
      resolve(EthereumIdentityProvider.web3.eth.accounts.recover(pubKeySignId, | 
				
			|||
          identity.signatures.publicKey) === identity.id); | 
				
			|||
    }); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // Initialize by supplying a web3 object
 | 
				
			|||
  static setWeb3(web3) { | 
				
			|||
    EthereumIdentityProvider.web3 = web3; | 
				
			|||
  } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
EthereumIdentityProvider.web3 = {}; | 
				
			|||
 | 
				
			|||
export default EthereumIdentityProvider; | 
				
			|||
								
									
										File diff suppressed because it is too large
									
								
							
						
					
					Loading…
					
					
				
		Reference in new issue