├── .env ├── .env.development ├── .env.production ├── .gitattributes ├── .gitignore ├── README.md ├── babel.config.js ├── build └── contracts │ ├── Base64.json │ ├── Identity.json │ ├── JWKS.json │ ├── JWT.json │ ├── JsmnSolLib.json │ ├── SolRsaVerify.json │ ├── StringUtils.json │ └── TestIdentity.json ├── contracts ├── Base64.sol ├── Identity.sol ├── JWKS.sol ├── JWT.sol ├── JsmnSolLib.sol ├── SolRsaVerify.sol ├── Strings.sol └── TestIdentity.sol ├── migrations └── .gitkeep ├── package-lock.json ├── package.json ├── private.pem ├── public.pem ├── public ├── _redirects ├── favicon.ico └── index.html ├── scripts ├── deployKeys.js └── test.sh ├── src ├── App.vue ├── components │ ├── CreateIdentity.vue │ ├── GoogleLogin.vue │ ├── Home.vue │ ├── Recover.vue │ ├── SignUp.vue │ └── YourAddress.vue ├── main.js └── utils │ ├── contracts.js │ └── jwt.js ├── test ├── identity.test.js ├── jwt.test.js └── rsaverify.test.js ├── truffle.js └── vue.config.js /.env: -------------------------------------------------------------------------------- 1 | VUE_APP_GOOGLE_CLIENT_ID= 2 | VUE_APP_JWKS_ADDRESS= 3 | VUE_APP_NETWORK= -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_GOOGLE_CLIENT_ID=548840269650-1euei0p1gh0jplab10gp9i16q3mml5up.apps.googleusercontent.com 2 | VUE_APP_JWKS_ADDRESS=0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B 3 | VUE_APP_NETWORK=1572097047502 -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_GOOGLE_CLIENT_ID=548840269650-b5v1cofflccm09e66setbpkc6vf26voa.apps.googleusercontent.com 2 | VUE_APP_JWKS_ADDRESS=0x80472ADD4d510Ce3dF14597b23b25849156f6004 3 | VUE_APP_NETWORK=4 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /db 5 | /.geth 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solidity JWT validator 2 | 3 | This project includes a set of contracts and a Vue-based demo that allow you to **deploy an identity contract** and then **recover it using Google Sign-In**. This is a proof-of-concept and is definitely not ready for production :warning: 4 | 5 | ## Project setup 6 | 7 | - Install dependencies via `npm install` 8 | - Create a project in the [Google Developers console](https://console.developers.google.com/) 9 | - In the Credentials section of your project, create an OAuth 2.0 for a Web Application 10 | - Copy the client ID to your `.env` local file 11 | - Start a local ganache instance and copy the network ID to your local `.env` file 12 | - Update the keys in `scripts/deployKeys.js` using the [latest JWKS shared by Google](https://accounts.google.com/.well-known/openid-configuration) 13 | - Deploy the JWKS contract running `npx truffle exec scripts/deployKeys.js --network local` and copy the deployment address to your local `.env` file 14 | - Run locally with `npm run serve`, or set up a production build with `npm run build` 15 | 16 | ## 3rd party smart contracts 17 | 18 | - https://github.com/adriamb/SolRsaVerify/ 19 | - https://github.com/chrisdotn/jsmnSol 20 | - https://github.com/Arachnid/solidity-stringutils/ 21 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /build/contracts/JWKS.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "JWKS", 3 | "abi": [ 4 | { 5 | "inputs": [], 6 | "payable": false, 7 | "stateMutability": "nonpayable", 8 | "type": "constructor" 9 | }, 10 | { 11 | "constant": false, 12 | "inputs": [ 13 | { 14 | "name": "kid", 15 | "type": "string" 16 | }, 17 | { 18 | "name": "modulus", 19 | "type": "bytes" 20 | } 21 | ], 22 | "name": "addKey", 23 | "outputs": [], 24 | "payable": false, 25 | "stateMutability": "nonpayable", 26 | "type": "function" 27 | }, 28 | { 29 | "constant": true, 30 | "inputs": [ 31 | { 32 | "name": "kid", 33 | "type": "string" 34 | } 35 | ], 36 | "name": "getModulus", 37 | "outputs": [ 38 | { 39 | "name": "", 40 | "type": "bytes" 41 | } 42 | ], 43 | "payable": false, 44 | "stateMutability": "view", 45 | "type": "function" 46 | } 47 | ], 48 | "metadata": "{\"compiler\":{\"version\":\"0.5.4+commit.9549d8ff\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[{\"name\":\"kid\",\"type\":\"string\"}],\"name\":\"getModulus\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"kid\",\"type\":\"string\"},{\"name\":\"modulus\",\"type\":\"bytes\"}],\"name\":\"addKey\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/home/spalladino/Projects/solidity-jwt/contracts/JWKS.sol\":\"JWKS\"},\"evmVersion\":\"byzantium\",\"libraries\":{},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/spalladino/Projects/solidity-jwt/contracts/JWKS.sol\":{\"keccak256\":\"0x2b5dffefb7a130c7f8acccbb9b5ad1ce491f420136d9145d1ed35083c543a761\",\"urls\":[\"bzzr://7ffbd19b5c28021a7f3151eeb55c710a5794b5a1f1fcfdf7cd05b38325cddacc\"]}},\"version\":1}", 49 | "bytecode": "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610540806100606000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063497fc8f414610058578063c4436bb51461018c575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506102de565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610151578082015181840152602081019050610136565b50505050905090810190601f16801561017e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102dc600480360360408110156101a257600080fd5b81019080803590602001906401000000008111156101bf57600080fd5b8201836020820111156101d157600080fd5b803590602001918460018302840111640100000000831117156101f357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561025657600080fd5b82018360208201111561026857600080fd5b8035906020019184600183028401116401000000008311171561028a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506103eb565b005b60606001826040518082805190602001908083835b60208310151561031857805182526020820191506020810190506020830392506102f3565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103df5780601f106103b4576101008083540402835291602001916103df565b820191906000526020600020905b8154815290600101906020018083116103c257829003601f168201915b50505050509050919050565b806001836040518082805190602001908083835b60208310151561042457805182526020820191506020810190506020830392506103ff565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051906020019061046a92919061046f565b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104b057805160ff19168380011785556104de565b828001600101855582156104de579182015b828111156104dd5782518255916020019190600101906104c2565b5b5090506104eb91906104ef565b5090565b61051191905b8082111561050d5760008160009055506001016104f5565b5090565b9056fea165627a7a723058208511b243706b86e3b7681ebe81dbf0057422a7cb9b65b08888d7f5e5eb8e80860029", 50 | "deployedBytecode": "0x608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063497fc8f414610058578063c4436bb51461018c575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506102de565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610151578082015181840152602081019050610136565b50505050905090810190601f16801561017e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102dc600480360360408110156101a257600080fd5b81019080803590602001906401000000008111156101bf57600080fd5b8201836020820111156101d157600080fd5b803590602001918460018302840111640100000000831117156101f357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561025657600080fd5b82018360208201111561026857600080fd5b8035906020019184600183028401116401000000008311171561028a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506103eb565b005b60606001826040518082805190602001908083835b60208310151561031857805182526020820191506020810190506020830392506102f3565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103df5780601f106103b4576101008083540402835291602001916103df565b820191906000526020600020905b8154815290600101906020018083116103c257829003601f168201915b50505050509050919050565b806001836040518082805190602001908083835b60208310151561042457805182526020820191506020810190506020830392506103ff565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051906020019061046a92919061046f565b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104b057805160ff19168380011785556104de565b828001600101855582156104de579182015b828111156104dd5782518255916020019190600101906104c2565b5b5090506104eb91906104ef565b5090565b61051191905b8082111561050d5760008160009055506001016104f5565b5090565b9056fea165627a7a723058208511b243706b86e3b7681ebe81dbf0057422a7cb9b65b08888d7f5e5eb8e80860029", 51 | "sourceMap": "23:327:2:-;;;95:50;8:9:-1;5:2;;;30:1;27;20:12;5:2;95:50:2;130:10;122:5;;:18;;;;;;;;;;;;;;;;;;23:327;;;;;;", 52 | "deployedSourceMap": "23:327:2:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;23:327:2;;;;;;;;;;;;;;;;;;;;;;;;;247:101;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;247:101:2;;;;;;;;;;21:11:-1;8;5:28;2:2;;;46:1;43;36:12;2:2;247:101:2;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;247:101:2;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;39:11;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;247:101:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;93:3;85:6;81:16;74:27;137:4;133:9;126:4;121:3;117:14;113:30;106:37;;169:3;161:6;157:16;147:26;;247:101:2;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;247:101:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;149:94;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;149:94:2;;;;;;;;;;21:11:-1;8;5:28;2:2;;;46:1;43;36:12;2:2;149:94:2;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;149:94:2;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;39:11;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;149:94:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;93:3;85:6;81:16;74:27;137:4;133:9;126:4;121:3;117:14;113:30;106:37;;169:3;161:6;157:16;147:26;;149:94:2;;;;;;;;;;;;;;;;;21:11:-1;8;5:28;2:2;;;46:1;43;36:12;2:2;149:94:2;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;149:94:2;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;39:11;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;149:94:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;93:3;85:6;81:16;74:27;137:4;133:9;126:4;121:3;117:14;113:30;106:37;;169:3;161:6;157:16;147:26;;149:94:2;;;;;;;;;;;;;;;:::i;:::-;;247:101;307:12;334:4;339:3;334:9;;;;;;;;;;;;;36:153:-1;66:2;61:3;58:11;51:19;36:153;;;182:3;176:10;171:3;164:23;98:2;93:3;89:12;82:19;;123:2;118:3;114:12;107:19;;148:2;143:3;139:12;132:19;;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;334:9:2;;;;;;;;;;;;;;;;;;;;;327:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;247:101;;;:::o;149:94::-;231:7;219:4;224:3;219:9;;;;;;;;;;;;;36:153:-1;66:2;61:3;58:11;51:19;36:153;;;182:3;176:10;171:3;164:23;98:2;93:3;89:12;82:19;;123:2;118:3;114:12;107:19;;148:2;143:3;139:12;132:19;;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;219:9:2;;;;;;;;;;;;;;;;;;;;;:19;;;;;;;;;;;;:::i;:::-;;149:94;;:::o;23:327::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o", 53 | "source": "pragma solidity ^0.5;\n\ncontract JWKS {\n address admin;\n \n mapping(string => bytes) keys;\n\n constructor() public {\n admin = msg.sender;\n }\n\n function addKey(string memory kid, bytes memory modulus) public {\n keys[kid] = modulus;\n }\n\n function getModulus(string memory kid) public view returns (bytes memory) {\n return keys[kid];\n }\n}", 54 | "sourcePath": "/home/spalladino/Projects/solidity-jwt/contracts/JWKS.sol", 55 | "ast": { 56 | "absolutePath": "/home/spalladino/Projects/solidity-jwt/contracts/JWKS.sol", 57 | "exportedSymbols": { 58 | "JWKS": [ 59 | 896 60 | ] 61 | }, 62 | "id": 897, 63 | "nodeType": "SourceUnit", 64 | "nodes": [ 65 | { 66 | "id": 854, 67 | "literals": [ 68 | "solidity", 69 | "^", 70 | "0.5" 71 | ], 72 | "nodeType": "PragmaDirective", 73 | "src": "0:21:2" 74 | }, 75 | { 76 | "baseContracts": [], 77 | "contractDependencies": [], 78 | "contractKind": "contract", 79 | "documentation": null, 80 | "fullyImplemented": true, 81 | "id": 896, 82 | "linearizedBaseContracts": [ 83 | 896 84 | ], 85 | "name": "JWKS", 86 | "nodeType": "ContractDefinition", 87 | "nodes": [ 88 | { 89 | "constant": false, 90 | "id": 856, 91 | "name": "admin", 92 | "nodeType": "VariableDeclaration", 93 | "scope": 896, 94 | "src": "41:13:2", 95 | "stateVariable": true, 96 | "storageLocation": "default", 97 | "typeDescriptions": { 98 | "typeIdentifier": "t_address", 99 | "typeString": "address" 100 | }, 101 | "typeName": { 102 | "id": 855, 103 | "name": "address", 104 | "nodeType": "ElementaryTypeName", 105 | "src": "41:7:2", 106 | "stateMutability": "nonpayable", 107 | "typeDescriptions": { 108 | "typeIdentifier": "t_address", 109 | "typeString": "address" 110 | } 111 | }, 112 | "value": null, 113 | "visibility": "internal" 114 | }, 115 | { 116 | "constant": false, 117 | "id": 860, 118 | "name": "keys", 119 | "nodeType": "VariableDeclaration", 120 | "scope": 896, 121 | "src": "61:29:2", 122 | "stateVariable": true, 123 | "storageLocation": "default", 124 | "typeDescriptions": { 125 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 126 | "typeString": "mapping(string => bytes)" 127 | }, 128 | "typeName": { 129 | "id": 859, 130 | "keyType": { 131 | "id": 857, 132 | "name": "string", 133 | "nodeType": "ElementaryTypeName", 134 | "src": "69:6:2", 135 | "typeDescriptions": { 136 | "typeIdentifier": "t_string_storage_ptr", 137 | "typeString": "string" 138 | } 139 | }, 140 | "nodeType": "Mapping", 141 | "src": "61:24:2", 142 | "typeDescriptions": { 143 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 144 | "typeString": "mapping(string => bytes)" 145 | }, 146 | "valueType": { 147 | "id": 858, 148 | "name": "bytes", 149 | "nodeType": "ElementaryTypeName", 150 | "src": "79:5:2", 151 | "typeDescriptions": { 152 | "typeIdentifier": "t_bytes_storage_ptr", 153 | "typeString": "bytes" 154 | } 155 | } 156 | }, 157 | "value": null, 158 | "visibility": "internal" 159 | }, 160 | { 161 | "body": { 162 | "id": 868, 163 | "nodeType": "Block", 164 | "src": "116:29:2", 165 | "statements": [ 166 | { 167 | "expression": { 168 | "argumentTypes": null, 169 | "id": 866, 170 | "isConstant": false, 171 | "isLValue": false, 172 | "isPure": false, 173 | "lValueRequested": false, 174 | "leftHandSide": { 175 | "argumentTypes": null, 176 | "id": 863, 177 | "name": "admin", 178 | "nodeType": "Identifier", 179 | "overloadedDeclarations": [], 180 | "referencedDeclaration": 856, 181 | "src": "122:5:2", 182 | "typeDescriptions": { 183 | "typeIdentifier": "t_address", 184 | "typeString": "address" 185 | } 186 | }, 187 | "nodeType": "Assignment", 188 | "operator": "=", 189 | "rightHandSide": { 190 | "argumentTypes": null, 191 | "expression": { 192 | "argumentTypes": null, 193 | "id": 864, 194 | "name": "msg", 195 | "nodeType": "Identifier", 196 | "overloadedDeclarations": [], 197 | "referencedDeclaration": 4698, 198 | "src": "130:3:2", 199 | "typeDescriptions": { 200 | "typeIdentifier": "t_magic_message", 201 | "typeString": "msg" 202 | } 203 | }, 204 | "id": 865, 205 | "isConstant": false, 206 | "isLValue": false, 207 | "isPure": false, 208 | "lValueRequested": false, 209 | "memberName": "sender", 210 | "nodeType": "MemberAccess", 211 | "referencedDeclaration": null, 212 | "src": "130:10:2", 213 | "typeDescriptions": { 214 | "typeIdentifier": "t_address_payable", 215 | "typeString": "address payable" 216 | } 217 | }, 218 | "src": "122:18:2", 219 | "typeDescriptions": { 220 | "typeIdentifier": "t_address", 221 | "typeString": "address" 222 | } 223 | }, 224 | "id": 867, 225 | "nodeType": "ExpressionStatement", 226 | "src": "122:18:2" 227 | } 228 | ] 229 | }, 230 | "documentation": null, 231 | "id": 869, 232 | "implemented": true, 233 | "kind": "constructor", 234 | "modifiers": [], 235 | "name": "", 236 | "nodeType": "FunctionDefinition", 237 | "parameters": { 238 | "id": 861, 239 | "nodeType": "ParameterList", 240 | "parameters": [], 241 | "src": "106:2:2" 242 | }, 243 | "returnParameters": { 244 | "id": 862, 245 | "nodeType": "ParameterList", 246 | "parameters": [], 247 | "src": "116:0:2" 248 | }, 249 | "scope": 896, 250 | "src": "95:50:2", 251 | "stateMutability": "nonpayable", 252 | "superFunction": null, 253 | "visibility": "public" 254 | }, 255 | { 256 | "body": { 257 | "id": 882, 258 | "nodeType": "Block", 259 | "src": "213:30:2", 260 | "statements": [ 261 | { 262 | "expression": { 263 | "argumentTypes": null, 264 | "id": 880, 265 | "isConstant": false, 266 | "isLValue": false, 267 | "isPure": false, 268 | "lValueRequested": false, 269 | "leftHandSide": { 270 | "argumentTypes": null, 271 | "baseExpression": { 272 | "argumentTypes": null, 273 | "id": 876, 274 | "name": "keys", 275 | "nodeType": "Identifier", 276 | "overloadedDeclarations": [], 277 | "referencedDeclaration": 860, 278 | "src": "219:4:2", 279 | "typeDescriptions": { 280 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 281 | "typeString": "mapping(string memory => bytes storage ref)" 282 | } 283 | }, 284 | "id": 878, 285 | "indexExpression": { 286 | "argumentTypes": null, 287 | "id": 877, 288 | "name": "kid", 289 | "nodeType": "Identifier", 290 | "overloadedDeclarations": [], 291 | "referencedDeclaration": 871, 292 | "src": "224:3:2", 293 | "typeDescriptions": { 294 | "typeIdentifier": "t_string_memory_ptr", 295 | "typeString": "string memory" 296 | } 297 | }, 298 | "isConstant": false, 299 | "isLValue": true, 300 | "isPure": false, 301 | "lValueRequested": true, 302 | "nodeType": "IndexAccess", 303 | "src": "219:9:2", 304 | "typeDescriptions": { 305 | "typeIdentifier": "t_bytes_storage", 306 | "typeString": "bytes storage ref" 307 | } 308 | }, 309 | "nodeType": "Assignment", 310 | "operator": "=", 311 | "rightHandSide": { 312 | "argumentTypes": null, 313 | "id": 879, 314 | "name": "modulus", 315 | "nodeType": "Identifier", 316 | "overloadedDeclarations": [], 317 | "referencedDeclaration": 873, 318 | "src": "231:7:2", 319 | "typeDescriptions": { 320 | "typeIdentifier": "t_bytes_memory_ptr", 321 | "typeString": "bytes memory" 322 | } 323 | }, 324 | "src": "219:19:2", 325 | "typeDescriptions": { 326 | "typeIdentifier": "t_bytes_storage", 327 | "typeString": "bytes storage ref" 328 | } 329 | }, 330 | "id": 881, 331 | "nodeType": "ExpressionStatement", 332 | "src": "219:19:2" 333 | } 334 | ] 335 | }, 336 | "documentation": null, 337 | "id": 883, 338 | "implemented": true, 339 | "kind": "function", 340 | "modifiers": [], 341 | "name": "addKey", 342 | "nodeType": "FunctionDefinition", 343 | "parameters": { 344 | "id": 874, 345 | "nodeType": "ParameterList", 346 | "parameters": [ 347 | { 348 | "constant": false, 349 | "id": 871, 350 | "name": "kid", 351 | "nodeType": "VariableDeclaration", 352 | "scope": 883, 353 | "src": "165:17:2", 354 | "stateVariable": false, 355 | "storageLocation": "memory", 356 | "typeDescriptions": { 357 | "typeIdentifier": "t_string_memory_ptr", 358 | "typeString": "string" 359 | }, 360 | "typeName": { 361 | "id": 870, 362 | "name": "string", 363 | "nodeType": "ElementaryTypeName", 364 | "src": "165:6:2", 365 | "typeDescriptions": { 366 | "typeIdentifier": "t_string_storage_ptr", 367 | "typeString": "string" 368 | } 369 | }, 370 | "value": null, 371 | "visibility": "internal" 372 | }, 373 | { 374 | "constant": false, 375 | "id": 873, 376 | "name": "modulus", 377 | "nodeType": "VariableDeclaration", 378 | "scope": 883, 379 | "src": "184:20:2", 380 | "stateVariable": false, 381 | "storageLocation": "memory", 382 | "typeDescriptions": { 383 | "typeIdentifier": "t_bytes_memory_ptr", 384 | "typeString": "bytes" 385 | }, 386 | "typeName": { 387 | "id": 872, 388 | "name": "bytes", 389 | "nodeType": "ElementaryTypeName", 390 | "src": "184:5:2", 391 | "typeDescriptions": { 392 | "typeIdentifier": "t_bytes_storage_ptr", 393 | "typeString": "bytes" 394 | } 395 | }, 396 | "value": null, 397 | "visibility": "internal" 398 | } 399 | ], 400 | "src": "164:41:2" 401 | }, 402 | "returnParameters": { 403 | "id": 875, 404 | "nodeType": "ParameterList", 405 | "parameters": [], 406 | "src": "213:0:2" 407 | }, 408 | "scope": 896, 409 | "src": "149:94:2", 410 | "stateMutability": "nonpayable", 411 | "superFunction": null, 412 | "visibility": "public" 413 | }, 414 | { 415 | "body": { 416 | "id": 894, 417 | "nodeType": "Block", 418 | "src": "321:27:2", 419 | "statements": [ 420 | { 421 | "expression": { 422 | "argumentTypes": null, 423 | "baseExpression": { 424 | "argumentTypes": null, 425 | "id": 890, 426 | "name": "keys", 427 | "nodeType": "Identifier", 428 | "overloadedDeclarations": [], 429 | "referencedDeclaration": 860, 430 | "src": "334:4:2", 431 | "typeDescriptions": { 432 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 433 | "typeString": "mapping(string memory => bytes storage ref)" 434 | } 435 | }, 436 | "id": 892, 437 | "indexExpression": { 438 | "argumentTypes": null, 439 | "id": 891, 440 | "name": "kid", 441 | "nodeType": "Identifier", 442 | "overloadedDeclarations": [], 443 | "referencedDeclaration": 885, 444 | "src": "339:3:2", 445 | "typeDescriptions": { 446 | "typeIdentifier": "t_string_memory_ptr", 447 | "typeString": "string memory" 448 | } 449 | }, 450 | "isConstant": false, 451 | "isLValue": true, 452 | "isPure": false, 453 | "lValueRequested": false, 454 | "nodeType": "IndexAccess", 455 | "src": "334:9:2", 456 | "typeDescriptions": { 457 | "typeIdentifier": "t_bytes_storage", 458 | "typeString": "bytes storage ref" 459 | } 460 | }, 461 | "functionReturnParameters": 889, 462 | "id": 893, 463 | "nodeType": "Return", 464 | "src": "327:16:2" 465 | } 466 | ] 467 | }, 468 | "documentation": null, 469 | "id": 895, 470 | "implemented": true, 471 | "kind": "function", 472 | "modifiers": [], 473 | "name": "getModulus", 474 | "nodeType": "FunctionDefinition", 475 | "parameters": { 476 | "id": 886, 477 | "nodeType": "ParameterList", 478 | "parameters": [ 479 | { 480 | "constant": false, 481 | "id": 885, 482 | "name": "kid", 483 | "nodeType": "VariableDeclaration", 484 | "scope": 895, 485 | "src": "267:17:2", 486 | "stateVariable": false, 487 | "storageLocation": "memory", 488 | "typeDescriptions": { 489 | "typeIdentifier": "t_string_memory_ptr", 490 | "typeString": "string" 491 | }, 492 | "typeName": { 493 | "id": 884, 494 | "name": "string", 495 | "nodeType": "ElementaryTypeName", 496 | "src": "267:6:2", 497 | "typeDescriptions": { 498 | "typeIdentifier": "t_string_storage_ptr", 499 | "typeString": "string" 500 | } 501 | }, 502 | "value": null, 503 | "visibility": "internal" 504 | } 505 | ], 506 | "src": "266:19:2" 507 | }, 508 | "returnParameters": { 509 | "id": 889, 510 | "nodeType": "ParameterList", 511 | "parameters": [ 512 | { 513 | "constant": false, 514 | "id": 888, 515 | "name": "", 516 | "nodeType": "VariableDeclaration", 517 | "scope": 895, 518 | "src": "307:12:2", 519 | "stateVariable": false, 520 | "storageLocation": "memory", 521 | "typeDescriptions": { 522 | "typeIdentifier": "t_bytes_memory_ptr", 523 | "typeString": "bytes" 524 | }, 525 | "typeName": { 526 | "id": 887, 527 | "name": "bytes", 528 | "nodeType": "ElementaryTypeName", 529 | "src": "307:5:2", 530 | "typeDescriptions": { 531 | "typeIdentifier": "t_bytes_storage_ptr", 532 | "typeString": "bytes" 533 | } 534 | }, 535 | "value": null, 536 | "visibility": "internal" 537 | } 538 | ], 539 | "src": "306:14:2" 540 | }, 541 | "scope": 896, 542 | "src": "247:101:2", 543 | "stateMutability": "view", 544 | "superFunction": null, 545 | "visibility": "public" 546 | } 547 | ], 548 | "scope": 897, 549 | "src": "23:327:2" 550 | } 551 | ], 552 | "src": "0:350:2" 553 | }, 554 | "legacyAST": { 555 | "absolutePath": "/home/spalladino/Projects/solidity-jwt/contracts/JWKS.sol", 556 | "exportedSymbols": { 557 | "JWKS": [ 558 | 896 559 | ] 560 | }, 561 | "id": 897, 562 | "nodeType": "SourceUnit", 563 | "nodes": [ 564 | { 565 | "id": 854, 566 | "literals": [ 567 | "solidity", 568 | "^", 569 | "0.5" 570 | ], 571 | "nodeType": "PragmaDirective", 572 | "src": "0:21:2" 573 | }, 574 | { 575 | "baseContracts": [], 576 | "contractDependencies": [], 577 | "contractKind": "contract", 578 | "documentation": null, 579 | "fullyImplemented": true, 580 | "id": 896, 581 | "linearizedBaseContracts": [ 582 | 896 583 | ], 584 | "name": "JWKS", 585 | "nodeType": "ContractDefinition", 586 | "nodes": [ 587 | { 588 | "constant": false, 589 | "id": 856, 590 | "name": "admin", 591 | "nodeType": "VariableDeclaration", 592 | "scope": 896, 593 | "src": "41:13:2", 594 | "stateVariable": true, 595 | "storageLocation": "default", 596 | "typeDescriptions": { 597 | "typeIdentifier": "t_address", 598 | "typeString": "address" 599 | }, 600 | "typeName": { 601 | "id": 855, 602 | "name": "address", 603 | "nodeType": "ElementaryTypeName", 604 | "src": "41:7:2", 605 | "stateMutability": "nonpayable", 606 | "typeDescriptions": { 607 | "typeIdentifier": "t_address", 608 | "typeString": "address" 609 | } 610 | }, 611 | "value": null, 612 | "visibility": "internal" 613 | }, 614 | { 615 | "constant": false, 616 | "id": 860, 617 | "name": "keys", 618 | "nodeType": "VariableDeclaration", 619 | "scope": 896, 620 | "src": "61:29:2", 621 | "stateVariable": true, 622 | "storageLocation": "default", 623 | "typeDescriptions": { 624 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 625 | "typeString": "mapping(string => bytes)" 626 | }, 627 | "typeName": { 628 | "id": 859, 629 | "keyType": { 630 | "id": 857, 631 | "name": "string", 632 | "nodeType": "ElementaryTypeName", 633 | "src": "69:6:2", 634 | "typeDescriptions": { 635 | "typeIdentifier": "t_string_storage_ptr", 636 | "typeString": "string" 637 | } 638 | }, 639 | "nodeType": "Mapping", 640 | "src": "61:24:2", 641 | "typeDescriptions": { 642 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 643 | "typeString": "mapping(string => bytes)" 644 | }, 645 | "valueType": { 646 | "id": 858, 647 | "name": "bytes", 648 | "nodeType": "ElementaryTypeName", 649 | "src": "79:5:2", 650 | "typeDescriptions": { 651 | "typeIdentifier": "t_bytes_storage_ptr", 652 | "typeString": "bytes" 653 | } 654 | } 655 | }, 656 | "value": null, 657 | "visibility": "internal" 658 | }, 659 | { 660 | "body": { 661 | "id": 868, 662 | "nodeType": "Block", 663 | "src": "116:29:2", 664 | "statements": [ 665 | { 666 | "expression": { 667 | "argumentTypes": null, 668 | "id": 866, 669 | "isConstant": false, 670 | "isLValue": false, 671 | "isPure": false, 672 | "lValueRequested": false, 673 | "leftHandSide": { 674 | "argumentTypes": null, 675 | "id": 863, 676 | "name": "admin", 677 | "nodeType": "Identifier", 678 | "overloadedDeclarations": [], 679 | "referencedDeclaration": 856, 680 | "src": "122:5:2", 681 | "typeDescriptions": { 682 | "typeIdentifier": "t_address", 683 | "typeString": "address" 684 | } 685 | }, 686 | "nodeType": "Assignment", 687 | "operator": "=", 688 | "rightHandSide": { 689 | "argumentTypes": null, 690 | "expression": { 691 | "argumentTypes": null, 692 | "id": 864, 693 | "name": "msg", 694 | "nodeType": "Identifier", 695 | "overloadedDeclarations": [], 696 | "referencedDeclaration": 4698, 697 | "src": "130:3:2", 698 | "typeDescriptions": { 699 | "typeIdentifier": "t_magic_message", 700 | "typeString": "msg" 701 | } 702 | }, 703 | "id": 865, 704 | "isConstant": false, 705 | "isLValue": false, 706 | "isPure": false, 707 | "lValueRequested": false, 708 | "memberName": "sender", 709 | "nodeType": "MemberAccess", 710 | "referencedDeclaration": null, 711 | "src": "130:10:2", 712 | "typeDescriptions": { 713 | "typeIdentifier": "t_address_payable", 714 | "typeString": "address payable" 715 | } 716 | }, 717 | "src": "122:18:2", 718 | "typeDescriptions": { 719 | "typeIdentifier": "t_address", 720 | "typeString": "address" 721 | } 722 | }, 723 | "id": 867, 724 | "nodeType": "ExpressionStatement", 725 | "src": "122:18:2" 726 | } 727 | ] 728 | }, 729 | "documentation": null, 730 | "id": 869, 731 | "implemented": true, 732 | "kind": "constructor", 733 | "modifiers": [], 734 | "name": "", 735 | "nodeType": "FunctionDefinition", 736 | "parameters": { 737 | "id": 861, 738 | "nodeType": "ParameterList", 739 | "parameters": [], 740 | "src": "106:2:2" 741 | }, 742 | "returnParameters": { 743 | "id": 862, 744 | "nodeType": "ParameterList", 745 | "parameters": [], 746 | "src": "116:0:2" 747 | }, 748 | "scope": 896, 749 | "src": "95:50:2", 750 | "stateMutability": "nonpayable", 751 | "superFunction": null, 752 | "visibility": "public" 753 | }, 754 | { 755 | "body": { 756 | "id": 882, 757 | "nodeType": "Block", 758 | "src": "213:30:2", 759 | "statements": [ 760 | { 761 | "expression": { 762 | "argumentTypes": null, 763 | "id": 880, 764 | "isConstant": false, 765 | "isLValue": false, 766 | "isPure": false, 767 | "lValueRequested": false, 768 | "leftHandSide": { 769 | "argumentTypes": null, 770 | "baseExpression": { 771 | "argumentTypes": null, 772 | "id": 876, 773 | "name": "keys", 774 | "nodeType": "Identifier", 775 | "overloadedDeclarations": [], 776 | "referencedDeclaration": 860, 777 | "src": "219:4:2", 778 | "typeDescriptions": { 779 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 780 | "typeString": "mapping(string memory => bytes storage ref)" 781 | } 782 | }, 783 | "id": 878, 784 | "indexExpression": { 785 | "argumentTypes": null, 786 | "id": 877, 787 | "name": "kid", 788 | "nodeType": "Identifier", 789 | "overloadedDeclarations": [], 790 | "referencedDeclaration": 871, 791 | "src": "224:3:2", 792 | "typeDescriptions": { 793 | "typeIdentifier": "t_string_memory_ptr", 794 | "typeString": "string memory" 795 | } 796 | }, 797 | "isConstant": false, 798 | "isLValue": true, 799 | "isPure": false, 800 | "lValueRequested": true, 801 | "nodeType": "IndexAccess", 802 | "src": "219:9:2", 803 | "typeDescriptions": { 804 | "typeIdentifier": "t_bytes_storage", 805 | "typeString": "bytes storage ref" 806 | } 807 | }, 808 | "nodeType": "Assignment", 809 | "operator": "=", 810 | "rightHandSide": { 811 | "argumentTypes": null, 812 | "id": 879, 813 | "name": "modulus", 814 | "nodeType": "Identifier", 815 | "overloadedDeclarations": [], 816 | "referencedDeclaration": 873, 817 | "src": "231:7:2", 818 | "typeDescriptions": { 819 | "typeIdentifier": "t_bytes_memory_ptr", 820 | "typeString": "bytes memory" 821 | } 822 | }, 823 | "src": "219:19:2", 824 | "typeDescriptions": { 825 | "typeIdentifier": "t_bytes_storage", 826 | "typeString": "bytes storage ref" 827 | } 828 | }, 829 | "id": 881, 830 | "nodeType": "ExpressionStatement", 831 | "src": "219:19:2" 832 | } 833 | ] 834 | }, 835 | "documentation": null, 836 | "id": 883, 837 | "implemented": true, 838 | "kind": "function", 839 | "modifiers": [], 840 | "name": "addKey", 841 | "nodeType": "FunctionDefinition", 842 | "parameters": { 843 | "id": 874, 844 | "nodeType": "ParameterList", 845 | "parameters": [ 846 | { 847 | "constant": false, 848 | "id": 871, 849 | "name": "kid", 850 | "nodeType": "VariableDeclaration", 851 | "scope": 883, 852 | "src": "165:17:2", 853 | "stateVariable": false, 854 | "storageLocation": "memory", 855 | "typeDescriptions": { 856 | "typeIdentifier": "t_string_memory_ptr", 857 | "typeString": "string" 858 | }, 859 | "typeName": { 860 | "id": 870, 861 | "name": "string", 862 | "nodeType": "ElementaryTypeName", 863 | "src": "165:6:2", 864 | "typeDescriptions": { 865 | "typeIdentifier": "t_string_storage_ptr", 866 | "typeString": "string" 867 | } 868 | }, 869 | "value": null, 870 | "visibility": "internal" 871 | }, 872 | { 873 | "constant": false, 874 | "id": 873, 875 | "name": "modulus", 876 | "nodeType": "VariableDeclaration", 877 | "scope": 883, 878 | "src": "184:20:2", 879 | "stateVariable": false, 880 | "storageLocation": "memory", 881 | "typeDescriptions": { 882 | "typeIdentifier": "t_bytes_memory_ptr", 883 | "typeString": "bytes" 884 | }, 885 | "typeName": { 886 | "id": 872, 887 | "name": "bytes", 888 | "nodeType": "ElementaryTypeName", 889 | "src": "184:5:2", 890 | "typeDescriptions": { 891 | "typeIdentifier": "t_bytes_storage_ptr", 892 | "typeString": "bytes" 893 | } 894 | }, 895 | "value": null, 896 | "visibility": "internal" 897 | } 898 | ], 899 | "src": "164:41:2" 900 | }, 901 | "returnParameters": { 902 | "id": 875, 903 | "nodeType": "ParameterList", 904 | "parameters": [], 905 | "src": "213:0:2" 906 | }, 907 | "scope": 896, 908 | "src": "149:94:2", 909 | "stateMutability": "nonpayable", 910 | "superFunction": null, 911 | "visibility": "public" 912 | }, 913 | { 914 | "body": { 915 | "id": 894, 916 | "nodeType": "Block", 917 | "src": "321:27:2", 918 | "statements": [ 919 | { 920 | "expression": { 921 | "argumentTypes": null, 922 | "baseExpression": { 923 | "argumentTypes": null, 924 | "id": 890, 925 | "name": "keys", 926 | "nodeType": "Identifier", 927 | "overloadedDeclarations": [], 928 | "referencedDeclaration": 860, 929 | "src": "334:4:2", 930 | "typeDescriptions": { 931 | "typeIdentifier": "t_mapping$_t_string_memory_$_t_bytes_storage_$", 932 | "typeString": "mapping(string memory => bytes storage ref)" 933 | } 934 | }, 935 | "id": 892, 936 | "indexExpression": { 937 | "argumentTypes": null, 938 | "id": 891, 939 | "name": "kid", 940 | "nodeType": "Identifier", 941 | "overloadedDeclarations": [], 942 | "referencedDeclaration": 885, 943 | "src": "339:3:2", 944 | "typeDescriptions": { 945 | "typeIdentifier": "t_string_memory_ptr", 946 | "typeString": "string memory" 947 | } 948 | }, 949 | "isConstant": false, 950 | "isLValue": true, 951 | "isPure": false, 952 | "lValueRequested": false, 953 | "nodeType": "IndexAccess", 954 | "src": "334:9:2", 955 | "typeDescriptions": { 956 | "typeIdentifier": "t_bytes_storage", 957 | "typeString": "bytes storage ref" 958 | } 959 | }, 960 | "functionReturnParameters": 889, 961 | "id": 893, 962 | "nodeType": "Return", 963 | "src": "327:16:2" 964 | } 965 | ] 966 | }, 967 | "documentation": null, 968 | "id": 895, 969 | "implemented": true, 970 | "kind": "function", 971 | "modifiers": [], 972 | "name": "getModulus", 973 | "nodeType": "FunctionDefinition", 974 | "parameters": { 975 | "id": 886, 976 | "nodeType": "ParameterList", 977 | "parameters": [ 978 | { 979 | "constant": false, 980 | "id": 885, 981 | "name": "kid", 982 | "nodeType": "VariableDeclaration", 983 | "scope": 895, 984 | "src": "267:17:2", 985 | "stateVariable": false, 986 | "storageLocation": "memory", 987 | "typeDescriptions": { 988 | "typeIdentifier": "t_string_memory_ptr", 989 | "typeString": "string" 990 | }, 991 | "typeName": { 992 | "id": 884, 993 | "name": "string", 994 | "nodeType": "ElementaryTypeName", 995 | "src": "267:6:2", 996 | "typeDescriptions": { 997 | "typeIdentifier": "t_string_storage_ptr", 998 | "typeString": "string" 999 | } 1000 | }, 1001 | "value": null, 1002 | "visibility": "internal" 1003 | } 1004 | ], 1005 | "src": "266:19:2" 1006 | }, 1007 | "returnParameters": { 1008 | "id": 889, 1009 | "nodeType": "ParameterList", 1010 | "parameters": [ 1011 | { 1012 | "constant": false, 1013 | "id": 888, 1014 | "name": "", 1015 | "nodeType": "VariableDeclaration", 1016 | "scope": 895, 1017 | "src": "307:12:2", 1018 | "stateVariable": false, 1019 | "storageLocation": "memory", 1020 | "typeDescriptions": { 1021 | "typeIdentifier": "t_bytes_memory_ptr", 1022 | "typeString": "bytes" 1023 | }, 1024 | "typeName": { 1025 | "id": 887, 1026 | "name": "bytes", 1027 | "nodeType": "ElementaryTypeName", 1028 | "src": "307:5:2", 1029 | "typeDescriptions": { 1030 | "typeIdentifier": "t_bytes_storage_ptr", 1031 | "typeString": "bytes" 1032 | } 1033 | }, 1034 | "value": null, 1035 | "visibility": "internal" 1036 | } 1037 | ], 1038 | "src": "306:14:2" 1039 | }, 1040 | "scope": 896, 1041 | "src": "247:101:2", 1042 | "stateMutability": "view", 1043 | "superFunction": null, 1044 | "visibility": "public" 1045 | } 1046 | ], 1047 | "scope": 897, 1048 | "src": "23:327:2" 1049 | } 1050 | ], 1051 | "src": "0:350:2" 1052 | }, 1053 | "compiler": { 1054 | "name": "solc", 1055 | "version": "0.5.4+commit.9549d8ff.Emscripten.clang" 1056 | }, 1057 | "networks": {}, 1058 | "schemaVersion": "3.0.6", 1059 | "updatedAt": "2019-10-26T13:56:39.733Z", 1060 | "devdoc": { 1061 | "methods": {} 1062 | }, 1063 | "userdoc": { 1064 | "methods": {} 1065 | } 1066 | } -------------------------------------------------------------------------------- /contracts/Base64.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | library Base64 { 4 | 5 | bytes constant private base64stdchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 6 | bytes constant private base64urlchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 7 | 8 | function encode(string memory _str) internal pure returns (string memory) { 9 | 10 | bytes memory _bs = bytes(_str); 11 | uint256 rem = _bs.length % 3; 12 | 13 | uint256 res_length = (_bs.length + 2) / 3 * 4 - ((3 - rem) % 3); 14 | bytes memory res = new bytes(res_length); 15 | 16 | uint256 i = 0; 17 | uint256 j = 0; 18 | 19 | for (; i + 3 <= _bs.length; i += 3) { 20 | (res[j], res[j+1], res[j+2], res[j+3]) = encode3( 21 | uint8(_bs[i]), 22 | uint8(_bs[i+1]), 23 | uint8(_bs[i+2]) 24 | ); 25 | 26 | j += 4; 27 | } 28 | 29 | if (rem != 0) { 30 | uint8 la0 = uint8(_bs[_bs.length - rem]); 31 | uint8 la1 = 0; 32 | 33 | if (rem == 2) { 34 | la1 = uint8(_bs[_bs.length - 1]); 35 | } 36 | 37 | (byte b0, byte b1, byte b2, byte b3) = encode3(la0, la1, 0); 38 | res[j] = b0; 39 | res[j+1] = b1; 40 | if (rem == 2) { 41 | res[j+2] = b2; 42 | } 43 | } 44 | 45 | return string(res); 46 | } 47 | 48 | function encode3(uint256 a0, uint256 a1, uint256 a2) 49 | private 50 | pure 51 | returns (byte b0, byte b1, byte b2, byte b3) 52 | { 53 | 54 | uint256 n = (a0 << 16) | (a1 << 8) | a2; 55 | 56 | uint256 c0 = (n >> 18) & 63; 57 | uint256 c1 = (n >> 12) & 63; 58 | uint256 c2 = (n >> 6) & 63; 59 | uint256 c3 = (n ) & 63; 60 | 61 | b0 = base64urlchars[c0]; 62 | b1 = base64urlchars[c1]; 63 | b2 = base64urlchars[c2]; 64 | b3 = base64urlchars[c3]; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /contracts/Identity.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./Base64.sol"; 4 | import "./JsmnSolLib.sol"; 5 | import "./JWT.sol"; 6 | import "./SolRsaVerify.sol"; 7 | import "./Strings.sol"; 8 | import "./JWKS.sol"; 9 | 10 | contract Identity { 11 | 12 | using Base64 for string; 13 | using StringUtils for *; 14 | using SolRsaVerify for *; 15 | using JsmnSolLib for string; 16 | 17 | mapping (address => bool) public accounts; 18 | address[] private accountsList; 19 | string public audience; 20 | string public subject; 21 | JWKS public keys; 22 | 23 | constructor(string memory sub, string memory aud, JWKS jwks) public payable { 24 | accounts[msg.sender] = true; 25 | accountsList.push(msg.sender); 26 | audience = aud; 27 | subject = sub; 28 | keys = jwks; 29 | } 30 | 31 | function recover(string memory headerJson, string memory payloadJson, bytes memory signature) public { 32 | string memory headerBase64 = headerJson.encode(); 33 | string memory payloadBase64 = payloadJson.encode(); 34 | StringUtils.slice[] memory slices = new StringUtils.slice[](2); 35 | slices[0] = headerBase64.toSlice(); 36 | slices[1] = payloadBase64.toSlice(); 37 | string memory message = ".".toSlice().join(slices); 38 | string memory kid = parseHeader(headerJson); 39 | bytes memory exponent = getRsaExponent(kid); 40 | bytes memory modulus = getRsaModulus(kid); 41 | require(message.pkcs1Sha256VerifyStr(signature, exponent, modulus) == 0, "RSA signature check failed"); 42 | 43 | (string memory aud, string memory nonce, string memory sub) = parseToken(payloadJson); 44 | 45 | require(aud.strCompare(audience) == 0 || true, "Audience does not match"); 46 | require(sub.strCompare(subject) == 0, "Subject does not match"); 47 | 48 | string memory senderBase64 = string(abi.encodePacked(msg.sender)).encode(); 49 | require(senderBase64.strCompare(nonce) == 0, "Sender does not match nonce"); 50 | 51 | if (!accounts[msg.sender]) { 52 | accounts[msg.sender] = true; 53 | accountsList.push(msg.sender); 54 | } 55 | } 56 | 57 | function parseHeader(string memory json) internal pure returns (string memory kid) { 58 | (uint exitCode, JsmnSolLib.Token[] memory tokens, uint ntokens) = json.parse(20); 59 | require(exitCode == 0, "JSON parse failed"); 60 | 61 | require(tokens[0].jsmnType == JsmnSolLib.JsmnType.OBJECT, "Expected JWT to be an object"); 62 | uint i = 1; 63 | while (i < ntokens) { 64 | require(tokens[i].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected JWT to contain only string keys"); 65 | string memory key = json.getBytes(tokens[i].start, tokens[i].end); 66 | if (key.strCompare("kid") == 0) { 67 | require(tokens[i+1].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected kid to be a string"); 68 | return json.getBytes(tokens[i+1].start, tokens[i+1].end); 69 | } 70 | i += 2; 71 | } 72 | } 73 | 74 | function parseToken(string memory json) internal pure returns (string memory aud, string memory nonce, string memory sub) { 75 | (uint exitCode, JsmnSolLib.Token[] memory tokens, uint ntokens) = json.parse(40); 76 | require(exitCode == 0, "JSON parse failed"); 77 | 78 | require(tokens[0].jsmnType == JsmnSolLib.JsmnType.OBJECT, "Expected JWT to be an object"); 79 | uint i = 1; 80 | while (i < ntokens) { 81 | require(tokens[i].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected JWT to contain only string keys"); 82 | string memory key = json.getBytes(tokens[i].start, tokens[i].end); 83 | if (key.strCompare("sub") == 0) { 84 | require(tokens[i+1].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected sub to be a string"); 85 | sub = json.getBytes(tokens[i+1].start, tokens[i+1].end); 86 | } else if (key.strCompare("aud") == 0) { 87 | require(tokens[i+1].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected aud to be a string"); 88 | aud = json.getBytes(tokens[i+1].start, tokens[i+1].end); 89 | } else if (key.strCompare("nonce") == 0) { 90 | require(tokens[i+1].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected nonce to be a string"); 91 | nonce = json.getBytes(tokens[i+1].start, tokens[i+1].end); 92 | } 93 | i += 2; 94 | } 95 | } 96 | 97 | function getRsaModulus(string memory kid) internal view returns (bytes memory modulus) { 98 | modulus = keys.getModulus(kid); 99 | if (modulus.length == 0) revert("Key not found"); 100 | } 101 | 102 | function getRsaExponent(string memory) internal pure returns (bytes memory) { 103 | return hex"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001"; 104 | } 105 | 106 | function getAccounts() public view returns (address[] memory) { 107 | return accountsList; 108 | } 109 | } -------------------------------------------------------------------------------- /contracts/JWKS.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5; 2 | 3 | contract JWKS { 4 | address admin; 5 | 6 | mapping(string => bytes) keys; 7 | 8 | constructor() public { 9 | admin = msg.sender; 10 | } 11 | 12 | function addKey(string memory kid, bytes memory modulus) public { 13 | keys[kid] = modulus; 14 | } 15 | 16 | function getModulus(string memory kid) public view returns (bytes memory) { 17 | return keys[kid]; 18 | } 19 | } -------------------------------------------------------------------------------- /contracts/JWT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./JsmnSolLib.sol"; 4 | 5 | contract JWT { 6 | using JsmnSolLib for string; 7 | 8 | function getSub(string memory json) public pure returns (string memory) { 9 | (uint exitCode, JsmnSolLib.Token[] memory tokens, uint ntokens) = json.parse(20); 10 | require(exitCode == 0, "JSON parse failed"); 11 | 12 | require(tokens[0].jsmnType == JsmnSolLib.JsmnType.OBJECT, "Expected JWT to be an object"); 13 | uint i = 1; 14 | while (i < ntokens) { 15 | require(tokens[i].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected JWT to contain only string keys"); 16 | string memory key = json.getBytes(tokens[i].start, tokens[i].end); 17 | if (key.strCompare("sub") == 0) { 18 | require(tokens[i+1].jsmnType == JsmnSolLib.JsmnType.STRING, "Expected sub to be a string"); 19 | return json.getBytes(tokens[i+1].start, tokens[i+1].end); 20 | } 21 | i += 2; 22 | } 23 | 24 | revert("Could not find sub in JWT object"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/JsmnSolLib.sol: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Christoph Niemann 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | pragma solidity ^0.5.0; 23 | 24 | library JsmnSolLib { 25 | 26 | enum JsmnType { UNDEFINED, OBJECT, ARRAY, STRING, PRIMITIVE } 27 | 28 | uint constant RETURN_SUCCESS = 0; 29 | uint constant RETURN_ERROR_INVALID_JSON = 1; 30 | uint constant RETURN_ERROR_PART = 2; 31 | uint constant RETURN_ERROR_NO_MEM = 3; 32 | 33 | struct Token { 34 | JsmnType jsmnType; 35 | uint start; 36 | bool startSet; 37 | uint end; 38 | bool endSet; 39 | uint8 size; 40 | } 41 | 42 | struct Parser { 43 | uint pos; 44 | uint toknext; 45 | int toksuper; 46 | } 47 | 48 | function init(uint length) internal pure returns (Parser memory, Token[] memory) { 49 | Parser memory p = Parser(0, 0, -1); 50 | Token[] memory t = new Token[](length); 51 | return (p, t); 52 | } 53 | 54 | function allocateToken(Parser memory parser, Token[] memory tokens) internal pure returns (bool, Token memory) { 55 | if (parser.toknext >= tokens.length) { 56 | // no more space in tokens 57 | return (false, tokens[tokens.length-1]); 58 | } 59 | Token memory token = Token(JsmnType.UNDEFINED, 0, false, 0, false, 0); 60 | tokens[parser.toknext] = token; 61 | parser.toknext++; 62 | return (true, token); 63 | } 64 | 65 | function fillToken(Token memory token, JsmnType jsmnType, uint start, uint end) internal pure { 66 | token.jsmnType = jsmnType; 67 | token.start = start; 68 | token.startSet = true; 69 | token.end = end; 70 | token.endSet = true; 71 | token.size = 0; 72 | } 73 | 74 | function parseString(Parser memory parser, Token[] memory tokens, bytes memory s) internal pure returns (uint) { 75 | uint start = parser.pos; 76 | bool success; 77 | Token memory token; 78 | parser.pos++; 79 | 80 | for (; parser.pos end of string 84 | if (c == '"') { 85 | (success, token) = allocateToken(parser, tokens); 86 | if (!success) { 87 | parser.pos = start; 88 | return RETURN_ERROR_NO_MEM; 89 | } 90 | fillToken(token, JsmnType.STRING, start+1, parser.pos); 91 | return RETURN_SUCCESS; 92 | } 93 | 94 | if (uint8(c) == 92 && parser.pos + 1 < s.length) { 95 | // handle escaped characters: skip over it 96 | parser.pos++; 97 | if (s[parser.pos] == '\"' || s[parser.pos] == '/' || s[parser.pos] == '\\' 98 | || s[parser.pos] == 'f' || s[parser.pos] == 'r' || s[parser.pos] == 'n' 99 | || s[parser.pos] == 'b' || s[parser.pos] == 't') { 100 | continue; 101 | } else { 102 | // all other values are INVALID 103 | parser.pos = start; 104 | return(RETURN_ERROR_INVALID_JSON); 105 | } 106 | } 107 | } 108 | parser.pos = start; 109 | return RETURN_ERROR_PART; 110 | } 111 | 112 | function parsePrimitive(Parser memory parser, Token[] memory tokens, bytes memory s) internal pure returns (uint) { 113 | bool found = false; 114 | uint start = parser.pos; 115 | byte c; 116 | bool success; 117 | Token memory token; 118 | for (; parser.pos < s.length; parser.pos++) { 119 | c = s[parser.pos]; 120 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' 121 | || c == 0x7d || c == 0x5d) { 122 | found = true; 123 | break; 124 | } 125 | if (uint8(c) < 32 || uint8(c) > 127) { 126 | parser.pos = start; 127 | return RETURN_ERROR_INVALID_JSON; 128 | } 129 | } 130 | if (!found) { 131 | parser.pos = start; 132 | return RETURN_ERROR_PART; 133 | } 134 | 135 | // found the end 136 | (success, token) = allocateToken(parser, tokens); 137 | if (!success) { 138 | parser.pos = start; 139 | return RETURN_ERROR_NO_MEM; 140 | } 141 | fillToken(token, JsmnType.PRIMITIVE, start, parser.pos); 142 | parser.pos--; 143 | return RETURN_SUCCESS; 144 | } 145 | 146 | function parse(string memory json, uint numberElements) internal pure returns (uint, Token[] memory tokens, uint) { 147 | bytes memory s = bytes(json); 148 | bool success; 149 | Parser memory parser; 150 | (parser, tokens) = init(numberElements); 151 | 152 | // Token memory token; 153 | uint r; 154 | uint count = parser.toknext; 155 | uint i; 156 | Token memory token; 157 | 158 | for (; parser.pos=0; i--) { 183 | token = tokens[i]; 184 | if (token.startSet && !token.endSet) { 185 | if (token.jsmnType != tokenType) { 186 | // found a token that hasn't been closed but from a different type 187 | return (RETURN_ERROR_INVALID_JSON, tokens, 0); 188 | } 189 | parser.toksuper = -1; 190 | tokens[i].end = parser.pos + 1; 191 | tokens[i].endSet = true; 192 | isUpdated = true; 193 | break; 194 | } 195 | } 196 | if (!isUpdated) { 197 | return (RETURN_ERROR_INVALID_JSON, tokens, 0); 198 | } 199 | for (; i>0; i--) { 200 | token = tokens[i]; 201 | if (token.startSet && !token.endSet) { 202 | parser.toksuper = int(i); 203 | break; 204 | } 205 | } 206 | 207 | if (i==0) { 208 | token = tokens[i]; 209 | if (token.startSet && !token.endSet) { 210 | parser.toksuper = uint128(i); 211 | } 212 | } 213 | continue; 214 | } 215 | 216 | // 0x42 217 | if (c == '"') { 218 | r = parseString(parser, tokens, s); 219 | 220 | if (r != RETURN_SUCCESS) { 221 | return (r, tokens, 0); 222 | } 223 | //JsmnError.INVALID; 224 | count++; 225 | if (parser.toksuper != -1) 226 | tokens[uint(parser.toksuper)].size++; 227 | continue; 228 | } 229 | 230 | // ' ', \r, \t, \n 231 | if (c == ' ' || c == 0x11 || c == 0x12 || c == 0x14) { 232 | continue; 233 | } 234 | 235 | // 0x3a 236 | if (c == ':') { 237 | parser.toksuper = int(parser.toknext -1); 238 | continue; 239 | } 240 | 241 | if (c == ',') { 242 | if (parser.toksuper != -1 243 | && tokens[uint(parser.toksuper)].jsmnType != JsmnType.ARRAY 244 | && tokens[uint(parser.toksuper)].jsmnType != JsmnType.OBJECT) { 245 | for(i = parser.toknext-1; i>=0; i--) { 246 | if (tokens[i].jsmnType == JsmnType.ARRAY || tokens[i].jsmnType == JsmnType.OBJECT) { 247 | if (tokens[i].startSet && !tokens[i].endSet) { 248 | parser.toksuper = int(i); 249 | break; 250 | } 251 | } 252 | } 253 | } 254 | continue; 255 | } 256 | 257 | // Primitive 258 | if ((c >= '0' && c <= '9') || c == '-' || c == 'f' || c == 't' || c == 'n') { 259 | if (parser.toksuper != -1) { 260 | token = tokens[uint(parser.toksuper)]; 261 | if (token.jsmnType == JsmnType.OBJECT 262 | || (token.jsmnType == JsmnType.STRING && token.size != 0)) { 263 | return (RETURN_ERROR_INVALID_JSON, tokens, 0); 264 | } 265 | } 266 | 267 | r = parsePrimitive(parser, tokens, s); 268 | if (r != RETURN_SUCCESS) { 269 | return (r, tokens, 0); 270 | } 271 | count++; 272 | if (parser.toksuper != -1) { 273 | tokens[uint(parser.toksuper)].size++; 274 | } 275 | continue; 276 | } 277 | 278 | // printable char 279 | if (c >= 0x20 && c <= 0x7e) { 280 | return (RETURN_ERROR_INVALID_JSON, tokens, 0); 281 | } 282 | } 283 | 284 | return (RETURN_SUCCESS, tokens, parser.toknext); 285 | } 286 | 287 | function getBytes(string memory json, uint start, uint end) internal pure returns (string memory) { 288 | bytes memory s = bytes(json); 289 | bytes memory result = new bytes(end-start); 290 | for (uint i=start; i= 48) && (uint8(bresult[i]) <= 57)) { 312 | if (decimals){ 313 | if (_b == 0) break; 314 | else _b--; 315 | } 316 | mint *= 10; 317 | mint += uint8(bresult[i]) - 48; 318 | } else if (uint8(bresult[i]) == 46) decimals = true; 319 | } 320 | if (_b > 0) mint *= int(10**_b); 321 | if (negative) mint *= -1; 322 | return mint; 323 | } 324 | 325 | function uint2str(uint i) internal pure returns (string memory){ 326 | if (i == 0) return "0"; 327 | uint j = i; 328 | uint len; 329 | while (j != 0){ 330 | len++; 331 | j /= 10; 332 | } 333 | bytes memory bstr = new bytes(len); 334 | uint k = len - 1; 335 | while (i != 0){ 336 | bstr[k--] = bytes1(uint8(48 + i % 10)); 337 | i /= 10; 338 | } 339 | return string(bstr); 340 | } 341 | 342 | function parseBool(string memory _a) internal pure returns (bool) { 343 | if (strCompare(_a, 'true') == 0) { 344 | return true; 345 | } else { 346 | return false; 347 | } 348 | } 349 | 350 | function strCompare(string memory _a, string memory _b) internal pure returns (int) { 351 | bytes memory a = bytes(_a); 352 | bytes memory b = bytes(_b); 353 | uint minLength = a.length; 354 | if (b.length < minLength) minLength = b.length; 355 | for (uint i = 0; i < minLength; i ++) 356 | if (a[i] < b[i]) 357 | return -1; 358 | else if (a[i] > b[i]) 359 | return 1; 360 | if (a.length < b.length) 361 | return -1; 362 | else if (a.length > b.length) 363 | return 1; 364 | else 365 | return 0; 366 | } 367 | 368 | } 369 | -------------------------------------------------------------------------------- /contracts/SolRsaVerify.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /* 4 | Copyright 2016, Adrià Massanet 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | 19 | Checked results with FIPS test vectors 20 | https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/dss/186-2rsatestvectors.zip 21 | file SigVer15_186-3.rsp 22 | 23 | */ 24 | 25 | library SolRsaVerify { 26 | function memcpy(uint _dest, uint _src, uint _len) pure internal { 27 | // Copy word-length chunks while possible 28 | for ( ;_len >= 32; _len -= 32) { 29 | assembly { 30 | mstore(_dest, mload(_src)) 31 | } 32 | _dest += 32; 33 | _src += 32; 34 | } 35 | 36 | // Copy remaining bytes 37 | uint mask = 256 ** (32 - _len) - 1; 38 | assembly { 39 | let srcpart := and(mload(_src), not(mask)) 40 | let destpart := and(mload(_dest), mask) 41 | mstore(_dest, or(destpart, srcpart)) 42 | } 43 | } 44 | 45 | 46 | function join( 47 | bytes memory _s, bytes memory _e, bytes memory _m 48 | ) pure internal returns (bytes memory) { 49 | uint inputLen = 0x60+_s.length+_e.length+_m.length; 50 | 51 | uint slen = _s.length; 52 | uint elen = _e.length; 53 | uint mlen = _m.length; 54 | uint sptr; 55 | uint eptr; 56 | uint mptr; 57 | uint inputPtr; 58 | 59 | bytes memory input = new bytes(inputLen); 60 | assembly { 61 | sptr := add(_s,0x20) 62 | eptr := add(_e,0x20) 63 | mptr := add(_m,0x20) 64 | mstore(add(input,0x20),slen) 65 | mstore(add(input,0x40),elen) 66 | mstore(add(input,0x60),mlen) 67 | inputPtr := add(input,0x20) 68 | } 69 | memcpy(inputPtr+0x60,sptr,_s.length); 70 | memcpy(inputPtr+0x60+_s.length,eptr,_e.length); 71 | memcpy(inputPtr+0x60+_s.length+_e.length,mptr,_m.length); 72 | 73 | return input; 74 | } 75 | 76 | /** @dev Verifies a PKCSv1.5 SSolRsaVerifyHA256 signature 77 | * @param _sha256 is the sha256 of the data 78 | * @param _s is the signature 79 | * @param _e is the exponent 80 | * @param _m is the modulus 81 | * @return 0 if success, >0 otherwise 82 | */ 83 | function pkcs1Sha256Verify( 84 | bytes32 _sha256, 85 | bytes memory _s, bytes memory _e, bytes memory _m 86 | ) public view returns (uint) { 87 | 88 | uint8[19] memory sha256Prefix = [ 89 | 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 90 | ]; 91 | 92 | require(_m.length >= sha256Prefix.length+_sha256.length+11); 93 | 94 | uint i; 95 | 96 | /// decipher 97 | bytes memory input = join(_s,_e,_m); 98 | uint inputlen = input.length; 99 | 100 | uint decipherlen = _m.length; 101 | bytes memory decipher = new bytes(decipherlen); 102 | assembly { 103 | pop(staticcall(sub(gas, 2000), 5, add(input,0x20), inputlen, add(decipher,0x20), decipherlen)) 104 | } 105 | 106 | /// 0x00 || 0x01 || PS || 0x00 || DigestInfo 107 | /// PS is padding filled with 0xff 108 | // DigestInfo ::= SEQUENCE { 109 | // digestAlgorithm AlgorithmIdentifier, 110 | // digest OCTET STRING 111 | // } 112 | 113 | uint paddingLen = decipherlen - 3 - sha256Prefix.length - 32; 114 | 115 | if (decipher[0] != 0 || uint8(decipher[1]) != 1) { 116 | return 1; 117 | } 118 | for (i = 2;i<2+paddingLen;i++) { 119 | if (decipher[i] != 0xff) { 120 | return 2; 121 | } 122 | } 123 | if (decipher[2+paddingLen] != 0) { 124 | return 3; 125 | } 126 | for (i = 0;i0 otherwise 146 | */ 147 | function pkcs1Sha256VerifyRaw( 148 | bytes memory _data, 149 | bytes memory _s, bytes memory _e, bytes memory _m 150 | ) internal view returns (uint) { 151 | return pkcs1Sha256Verify(sha256(_data),_s,_e,_m); 152 | } 153 | 154 | function pkcs1Sha256VerifyStr( 155 | string memory _data, 156 | bytes memory _s, bytes memory _e, bytes memory _m 157 | ) internal view returns (uint) { 158 | return pkcs1Sha256Verify(sha256(bytes(_data)),_s,_e,_m); 159 | } 160 | 161 | } -------------------------------------------------------------------------------- /contracts/Strings.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * @title String & slice utility library for Solidity contracts. 3 | * @author Nick Johnson 4 | * 5 | * @dev Functionality in this library is largely implemented using an 6 | * abstraction called a 'slice'. A slice represents a part of a string - 7 | * anything from the entire string to a single character, or even no 8 | * characters at all (a 0-length slice). Since a slice only has to specify 9 | * an offset and a length, copying and manipulating slices is a lot less 10 | * expensive than copying and manipulating the strings they reference. 11 | * 12 | * To further reduce gas costs, most functions on slice that need to return 13 | * a slice modify the original one instead of allocating a new one; for 14 | * instance, `s.split(".")` will return the text up to the first '.', 15 | * modifying s to only contain the remainder of the string after the '.'. 16 | * In situations where you do not want to modify the original slice, you 17 | * can make a copy first with `.copy()`, for example: 18 | * `s.copy().split(".")`. Try and avoid using this idiom in loops; since 19 | * Solidity has no memory management, it will result in allocating many 20 | * short-lived slices that are later discarded. 21 | * 22 | * Functions that return two slices come in two versions: a non-allocating 23 | * version that takes the second slice as an argument, modifying it in 24 | * place, and an allocating version that allocates and returns the second 25 | * slice; see `nextRune` for example. 26 | * 27 | * Functions that have to copy string data will return strings rather than 28 | * slices; these can be cast back to slices for further processing if 29 | * required. 30 | * 31 | * For convenience, some functions are provided with non-modifying 32 | * variants that create a new slice and return both; for instance, 33 | * `s.splitNew('.')` leaves s unmodified, and returns two values 34 | * corresponding to the left and right parts of the string. 35 | */ 36 | 37 | pragma solidity ^0.5.0; 38 | 39 | library StringUtils { 40 | struct slice { 41 | uint _len; 42 | uint _ptr; 43 | } 44 | 45 | function memcpy(uint dest, uint src, uint len) private pure { 46 | // Copy word-length chunks while possible 47 | for(; len >= 32; len -= 32) { 48 | assembly { 49 | mstore(dest, mload(src)) 50 | } 51 | dest += 32; 52 | src += 32; 53 | } 54 | 55 | // Copy remaining bytes 56 | uint mask = 256 ** (32 - len) - 1; 57 | assembly { 58 | let srcpart := and(mload(src), not(mask)) 59 | let destpart := and(mload(dest), mask) 60 | mstore(dest, or(destpart, srcpart)) 61 | } 62 | } 63 | 64 | /* 65 | * @dev Returns a slice containing the entire string. 66 | * @param self The string to make a slice from. 67 | * @return A newly allocated slice containing the entire string. 68 | */ 69 | function toSlice(string memory self) internal pure returns (slice memory) { 70 | uint ptr; 71 | assembly { 72 | ptr := add(self, 0x20) 73 | } 74 | return slice(bytes(self).length, ptr); 75 | } 76 | 77 | /* 78 | * @dev Returns the length of a null-terminated bytes32 string. 79 | * @param self The value to find the length of. 80 | * @return The length of the string, from 0 to 32. 81 | */ 82 | function len(bytes32 self) internal pure returns (uint) { 83 | uint ret; 84 | if (self == 0) 85 | return 0; 86 | if (self & bytes32(uint256(0xffffffffffffffffffffffffffffffff)) == 0) { 87 | ret += 16; 88 | self = bytes32(uint(self) / 0x100000000000000000000000000000000); 89 | } 90 | if (self & bytes32(uint256(0xffffffffffffffff)) == 0) { 91 | ret += 8; 92 | self = bytes32(uint(self) / 0x10000000000000000); 93 | } 94 | if (self & bytes32(uint256(0xffffffff)) == 0) { 95 | ret += 4; 96 | self = bytes32(uint(self) / 0x100000000); 97 | } 98 | if (self & bytes32(uint256(0xffff)) == 0) { 99 | ret += 2; 100 | self = bytes32(uint(self) / 0x10000); 101 | } 102 | if (self & bytes32(uint256(0xff)) == 0) { 103 | ret += 1; 104 | } 105 | return 32 - ret; 106 | } 107 | 108 | /* 109 | * @dev Returns a slice containing the entire bytes32, interpreted as a 110 | * null-terminated utf-8 string. 111 | * @param self The bytes32 value to convert to a slice. 112 | * @return A new slice containing the value of the input argument up to the 113 | * first null. 114 | */ 115 | function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { 116 | // Allocate space for `self` in memory, copy it there, and point ret at it 117 | assembly { 118 | let ptr := mload(0x40) 119 | mstore(0x40, add(ptr, 0x20)) 120 | mstore(ptr, self) 121 | mstore(add(ret, 0x20), ptr) 122 | } 123 | ret._len = len(self); 124 | } 125 | 126 | /* 127 | * @dev Returns a new slice containing the same data as the current slice. 128 | * @param self The slice to copy. 129 | * @return A new slice containing the same data as `self`. 130 | */ 131 | function copy(slice memory self) internal pure returns (slice memory) { 132 | return slice(self._len, self._ptr); 133 | } 134 | 135 | /* 136 | * @dev Copies a slice to a new string. 137 | * @param self The slice to copy. 138 | * @return A newly allocated string containing the slice's text. 139 | */ 140 | function toString(slice memory self) internal pure returns (string memory) { 141 | string memory ret = new string(self._len); 142 | uint retptr; 143 | assembly { retptr := add(ret, 32) } 144 | 145 | memcpy(retptr, self._ptr, self._len); 146 | return ret; 147 | } 148 | 149 | /* 150 | * @dev Returns the length in runes of the slice. Note that this operation 151 | * takes time proportional to the length of the slice; avoid using it 152 | * in loops, and call `slice.empty()` if you only need to know whether 153 | * the slice is empty or not. 154 | * @param self The slice to operate on. 155 | * @return The length of the slice in runes. 156 | */ 157 | function len(slice memory self) internal pure returns (uint l) { 158 | // Starting at ptr-31 means the LSB will be the byte we care about 159 | uint ptr = self._ptr - 31; 160 | uint end = ptr + self._len; 161 | for (l = 0; ptr < end; l++) { 162 | uint8 b; 163 | assembly { b := and(mload(ptr), 0xFF) } 164 | if (b < 0x80) { 165 | ptr += 1; 166 | } else if(b < 0xE0) { 167 | ptr += 2; 168 | } else if(b < 0xF0) { 169 | ptr += 3; 170 | } else if(b < 0xF8) { 171 | ptr += 4; 172 | } else if(b < 0xFC) { 173 | ptr += 5; 174 | } else { 175 | ptr += 6; 176 | } 177 | } 178 | } 179 | 180 | /* 181 | * @dev Returns true if the slice is empty (has a length of 0). 182 | * @param self The slice to operate on. 183 | * @return True if the slice is empty, False otherwise. 184 | */ 185 | function empty(slice memory self) internal pure returns (bool) { 186 | return self._len == 0; 187 | } 188 | 189 | /* 190 | * @dev Returns a positive number if `other` comes lexicographically after 191 | * `self`, a negative number if it comes before, or zero if the 192 | * contents of the two slices are equal. Comparison is done per-rune, 193 | * on unicode codepoints. 194 | * @param self The first slice to compare. 195 | * @param other The second slice to compare. 196 | * @return The result of the comparison. 197 | */ 198 | function compare(slice memory self, slice memory other) internal pure returns (int) { 199 | uint shortest = self._len; 200 | if (other._len < self._len) 201 | shortest = other._len; 202 | 203 | uint selfptr = self._ptr; 204 | uint otherptr = other._ptr; 205 | for (uint idx = 0; idx < shortest; idx += 32) { 206 | uint a; 207 | uint b; 208 | assembly { 209 | a := mload(selfptr) 210 | b := mload(otherptr) 211 | } 212 | if (a != b) { 213 | // Mask out irrelevant bytes and check again 214 | uint256 mask = uint256(-1); // 0xffff... 215 | if(shortest < 32) { 216 | mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); 217 | } 218 | uint256 diff = (a & mask) - (b & mask); 219 | if (diff != 0) 220 | return int(diff); 221 | } 222 | selfptr += 32; 223 | otherptr += 32; 224 | } 225 | return int(self._len) - int(other._len); 226 | } 227 | 228 | /* 229 | * @dev Returns true if the two slices contain the same text. 230 | * @param self The first slice to compare. 231 | * @param self The second slice to compare. 232 | * @return True if the slices are equal, false otherwise. 233 | */ 234 | function equals(slice memory self, slice memory other) internal pure returns (bool) { 235 | return compare(self, other) == 0; 236 | } 237 | 238 | /* 239 | * @dev Extracts the first rune in the slice into `rune`, advancing the 240 | * slice to point to the next rune and returning `self`. 241 | * @param self The slice to operate on. 242 | * @param rune The slice that will contain the first rune. 243 | * @return `rune`. 244 | */ 245 | function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { 246 | rune._ptr = self._ptr; 247 | 248 | if (self._len == 0) { 249 | rune._len = 0; 250 | return rune; 251 | } 252 | 253 | uint l; 254 | uint b; 255 | // Load the first byte of the rune into the LSBs of b 256 | assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } 257 | if (b < 0x80) { 258 | l = 1; 259 | } else if(b < 0xE0) { 260 | l = 2; 261 | } else if(b < 0xF0) { 262 | l = 3; 263 | } else { 264 | l = 4; 265 | } 266 | 267 | // Check for truncated codepoints 268 | if (l > self._len) { 269 | rune._len = self._len; 270 | self._ptr += self._len; 271 | self._len = 0; 272 | return rune; 273 | } 274 | 275 | self._ptr += l; 276 | self._len -= l; 277 | rune._len = l; 278 | return rune; 279 | } 280 | 281 | /* 282 | * @dev Returns the first rune in the slice, advancing the slice to point 283 | * to the next rune. 284 | * @param self The slice to operate on. 285 | * @return A slice containing only the first rune from `self`. 286 | */ 287 | function nextRune(slice memory self) internal pure returns (slice memory ret) { 288 | nextRune(self, ret); 289 | } 290 | 291 | /* 292 | * @dev Returns the number of the first codepoint in the slice. 293 | * @param self The slice to operate on. 294 | * @return The number of the first codepoint in the slice. 295 | */ 296 | function ord(slice memory self) internal pure returns (uint ret) { 297 | if (self._len == 0) { 298 | return 0; 299 | } 300 | 301 | uint word; 302 | uint length; 303 | uint divisor = 2 ** 248; 304 | 305 | // Load the rune into the MSBs of b 306 | assembly { word:= mload(mload(add(self, 32))) } 307 | uint b = word / divisor; 308 | if (b < 0x80) { 309 | ret = b; 310 | length = 1; 311 | } else if(b < 0xE0) { 312 | ret = b & 0x1F; 313 | length = 2; 314 | } else if(b < 0xF0) { 315 | ret = b & 0x0F; 316 | length = 3; 317 | } else { 318 | ret = b & 0x07; 319 | length = 4; 320 | } 321 | 322 | // Check for truncated codepoints 323 | if (length > self._len) { 324 | return 0; 325 | } 326 | 327 | for (uint i = 1; i < length; i++) { 328 | divisor = divisor / 256; 329 | b = (word / divisor) & 0xFF; 330 | if (b & 0xC0 != 0x80) { 331 | // Invalid UTF-8 sequence 332 | return 0; 333 | } 334 | ret = (ret * 64) | (b & 0x3F); 335 | } 336 | 337 | return ret; 338 | } 339 | 340 | /* 341 | * @dev Returns the keccak-256 hash of the slice. 342 | * @param self The slice to hash. 343 | * @return The hash of the slice. 344 | */ 345 | function keccak(slice memory self) internal pure returns (bytes32 ret) { 346 | assembly { 347 | ret := keccak256(mload(add(self, 32)), mload(self)) 348 | } 349 | } 350 | 351 | /* 352 | * @dev Returns true if `self` starts with `needle`. 353 | * @param self The slice to operate on. 354 | * @param needle The slice to search for. 355 | * @return True if the slice starts with the provided text, false otherwise. 356 | */ 357 | function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { 358 | if (self._len < needle._len) { 359 | return false; 360 | } 361 | 362 | if (self._ptr == needle._ptr) { 363 | return true; 364 | } 365 | 366 | bool equal; 367 | assembly { 368 | let length := mload(needle) 369 | let selfptr := mload(add(self, 0x20)) 370 | let needleptr := mload(add(needle, 0x20)) 371 | equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) 372 | } 373 | return equal; 374 | } 375 | 376 | /* 377 | * @dev If `self` starts with `needle`, `needle` is removed from the 378 | * beginning of `self`. Otherwise, `self` is unmodified. 379 | * @param self The slice to operate on. 380 | * @param needle The slice to search for. 381 | * @return `self` 382 | */ 383 | function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { 384 | if (self._len < needle._len) { 385 | return self; 386 | } 387 | 388 | bool equal = true; 389 | if (self._ptr != needle._ptr) { 390 | assembly { 391 | let length := mload(needle) 392 | let selfptr := mload(add(self, 0x20)) 393 | let needleptr := mload(add(needle, 0x20)) 394 | equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) 395 | } 396 | } 397 | 398 | if (equal) { 399 | self._len -= needle._len; 400 | self._ptr += needle._len; 401 | } 402 | 403 | return self; 404 | } 405 | 406 | /* 407 | * @dev Returns true if the slice ends with `needle`. 408 | * @param self The slice to operate on. 409 | * @param needle The slice to search for. 410 | * @return True if the slice starts with the provided text, false otherwise. 411 | */ 412 | function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { 413 | if (self._len < needle._len) { 414 | return false; 415 | } 416 | 417 | uint selfptr = self._ptr + self._len - needle._len; 418 | 419 | if (selfptr == needle._ptr) { 420 | return true; 421 | } 422 | 423 | bool equal; 424 | assembly { 425 | let length := mload(needle) 426 | let needleptr := mload(add(needle, 0x20)) 427 | equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) 428 | } 429 | 430 | return equal; 431 | } 432 | 433 | /* 434 | * @dev If `self` ends with `needle`, `needle` is removed from the 435 | * end of `self`. Otherwise, `self` is unmodified. 436 | * @param self The slice to operate on. 437 | * @param needle The slice to search for. 438 | * @return `self` 439 | */ 440 | function until(slice memory self, slice memory needle) internal pure returns (slice memory) { 441 | if (self._len < needle._len) { 442 | return self; 443 | } 444 | 445 | uint selfptr = self._ptr + self._len - needle._len; 446 | bool equal = true; 447 | if (selfptr != needle._ptr) { 448 | assembly { 449 | let length := mload(needle) 450 | let needleptr := mload(add(needle, 0x20)) 451 | equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) 452 | } 453 | } 454 | 455 | if (equal) { 456 | self._len -= needle._len; 457 | } 458 | 459 | return self; 460 | } 461 | 462 | // Returns the memory address of the first byte of the first occurrence of 463 | // `needle` in `self`, or the first byte after `self` if not found. 464 | function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { 465 | uint ptr = selfptr; 466 | uint idx; 467 | 468 | if (needlelen <= selflen) { 469 | if (needlelen <= 32) { 470 | bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); 471 | 472 | bytes32 needledata; 473 | assembly { needledata := and(mload(needleptr), mask) } 474 | 475 | uint end = selfptr + selflen - needlelen; 476 | bytes32 ptrdata; 477 | assembly { ptrdata := and(mload(ptr), mask) } 478 | 479 | while (ptrdata != needledata) { 480 | if (ptr >= end) 481 | return selfptr + selflen; 482 | ptr++; 483 | assembly { ptrdata := and(mload(ptr), mask) } 484 | } 485 | return ptr; 486 | } else { 487 | // For long needles, use hashing 488 | bytes32 hash; 489 | assembly { hash := keccak256(needleptr, needlelen) } 490 | 491 | for (idx = 0; idx <= selflen - needlelen; idx++) { 492 | bytes32 testHash; 493 | assembly { testHash := keccak256(ptr, needlelen) } 494 | if (hash == testHash) 495 | return ptr; 496 | ptr += 1; 497 | } 498 | } 499 | } 500 | return selfptr + selflen; 501 | } 502 | 503 | // Returns the memory address of the first byte after the last occurrence of 504 | // `needle` in `self`, or the address of `self` if not found. 505 | function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { 506 | uint ptr; 507 | 508 | if (needlelen <= selflen) { 509 | if (needlelen <= 32) { 510 | bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); 511 | 512 | bytes32 needledata; 513 | assembly { needledata := and(mload(needleptr), mask) } 514 | 515 | ptr = selfptr + selflen - needlelen; 516 | bytes32 ptrdata; 517 | assembly { ptrdata := and(mload(ptr), mask) } 518 | 519 | while (ptrdata != needledata) { 520 | if (ptr <= selfptr) 521 | return selfptr; 522 | ptr--; 523 | assembly { ptrdata := and(mload(ptr), mask) } 524 | } 525 | return ptr + needlelen; 526 | } else { 527 | // For long needles, use hashing 528 | bytes32 hash; 529 | assembly { hash := keccak256(needleptr, needlelen) } 530 | ptr = selfptr + (selflen - needlelen); 531 | while (ptr >= selfptr) { 532 | bytes32 testHash; 533 | assembly { testHash := keccak256(ptr, needlelen) } 534 | if (hash == testHash) 535 | return ptr + needlelen; 536 | ptr -= 1; 537 | } 538 | } 539 | } 540 | return selfptr; 541 | } 542 | 543 | /* 544 | * @dev Modifies `self` to contain everything from the first occurrence of 545 | * `needle` to the end of the slice. `self` is set to the empty slice 546 | * if `needle` is not found. 547 | * @param self The slice to search and modify. 548 | * @param needle The text to search for. 549 | * @return `self`. 550 | */ 551 | function find(slice memory self, slice memory needle) internal pure returns (slice memory) { 552 | uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); 553 | self._len -= ptr - self._ptr; 554 | self._ptr = ptr; 555 | return self; 556 | } 557 | 558 | /* 559 | * @dev Modifies `self` to contain the part of the string from the start of 560 | * `self` to the end of the first occurrence of `needle`. If `needle` 561 | * is not found, `self` is set to the empty slice. 562 | * @param self The slice to search and modify. 563 | * @param needle The text to search for. 564 | * @return `self`. 565 | */ 566 | function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { 567 | uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); 568 | self._len = ptr - self._ptr; 569 | return self; 570 | } 571 | 572 | /* 573 | * @dev Splits the slice, setting `self` to everything after the first 574 | * occurrence of `needle`, and `token` to everything before it. If 575 | * `needle` does not occur in `self`, `self` is set to the empty slice, 576 | * and `token` is set to the entirety of `self`. 577 | * @param self The slice to split. 578 | * @param needle The text to search for in `self`. 579 | * @param token An output parameter to which the first token is written. 580 | * @return `token`. 581 | */ 582 | function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { 583 | uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); 584 | token._ptr = self._ptr; 585 | token._len = ptr - self._ptr; 586 | if (ptr == self._ptr + self._len) { 587 | // Not found 588 | self._len = 0; 589 | } else { 590 | self._len -= token._len + needle._len; 591 | self._ptr = ptr + needle._len; 592 | } 593 | return token; 594 | } 595 | 596 | /* 597 | * @dev Splits the slice, setting `self` to everything after the first 598 | * occurrence of `needle`, and returning everything before it. If 599 | * `needle` does not occur in `self`, `self` is set to the empty slice, 600 | * and the entirety of `self` is returned. 601 | * @param self The slice to split. 602 | * @param needle The text to search for in `self`. 603 | * @return The part of `self` up to the first occurrence of `delim`. 604 | */ 605 | function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { 606 | split(self, needle, token); 607 | } 608 | 609 | /* 610 | * @dev Splits the slice, setting `self` to everything before the last 611 | * occurrence of `needle`, and `token` to everything after it. If 612 | * `needle` does not occur in `self`, `self` is set to the empty slice, 613 | * and `token` is set to the entirety of `self`. 614 | * @param self The slice to split. 615 | * @param needle The text to search for in `self`. 616 | * @param token An output parameter to which the first token is written. 617 | * @return `token`. 618 | */ 619 | function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { 620 | uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); 621 | token._ptr = ptr; 622 | token._len = self._len - (ptr - self._ptr); 623 | if (ptr == self._ptr) { 624 | // Not found 625 | self._len = 0; 626 | } else { 627 | self._len -= token._len + needle._len; 628 | } 629 | return token; 630 | } 631 | 632 | /* 633 | * @dev Splits the slice, setting `self` to everything before the last 634 | * occurrence of `needle`, and returning everything after it. If 635 | * `needle` does not occur in `self`, `self` is set to the empty slice, 636 | * and the entirety of `self` is returned. 637 | * @param self The slice to split. 638 | * @param needle The text to search for in `self`. 639 | * @return The part of `self` after the last occurrence of `delim`. 640 | */ 641 | function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { 642 | rsplit(self, needle, token); 643 | } 644 | 645 | /* 646 | * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. 647 | * @param self The slice to search. 648 | * @param needle The text to search for in `self`. 649 | * @return The number of occurrences of `needle` found in `self`. 650 | */ 651 | function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { 652 | uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; 653 | while (ptr <= self._ptr + self._len) { 654 | cnt++; 655 | ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; 656 | } 657 | } 658 | 659 | /* 660 | * @dev Returns True if `self` contains `needle`. 661 | * @param self The slice to search. 662 | * @param needle The text to search for in `self`. 663 | * @return True if `needle` is found in `self`, false otherwise. 664 | */ 665 | function contains(slice memory self, slice memory needle) internal pure returns (bool) { 666 | return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; 667 | } 668 | 669 | /* 670 | * @dev Returns a newly allocated string containing the concatenation of 671 | * `self` and `other`. 672 | * @param self The first slice to concatenate. 673 | * @param other The second slice to concatenate. 674 | * @return The concatenation of the two strings. 675 | */ 676 | function concat(slice memory self, slice memory other) internal pure returns (string memory) { 677 | string memory ret = new string(self._len + other._len); 678 | uint retptr; 679 | assembly { retptr := add(ret, 32) } 680 | memcpy(retptr, self._ptr, self._len); 681 | memcpy(retptr + self._len, other._ptr, other._len); 682 | return ret; 683 | } 684 | 685 | /* 686 | * @dev Joins an array of slices, using `self` as a delimiter, returning a 687 | * newly allocated string. 688 | * @param self The delimiter to use. 689 | * @param parts A list of slices to join. 690 | * @return A newly allocated string containing all the slices in `parts`, 691 | * joined with `self`. 692 | */ 693 | function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { 694 | if (parts.length == 0) 695 | return ""; 696 | 697 | uint length = self._len * (parts.length - 1); 698 | for(uint i = 0; i < parts.length; i++) 699 | length += parts[i]._len; 700 | 701 | string memory ret = new string(length); 702 | uint retptr; 703 | assembly { retptr := add(ret, 32) } 704 | 705 | for(uint i = 0; i < parts.length; i++) { 706 | memcpy(retptr, parts[i]._ptr, parts[i]._len); 707 | retptr += parts[i]._len; 708 | if (i < parts.length - 1) { 709 | memcpy(retptr, self._ptr, self._len); 710 | retptr += self._len; 711 | } 712 | } 713 | 714 | return ret; 715 | } 716 | } -------------------------------------------------------------------------------- /contracts/TestIdentity.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./Identity.sol"; 4 | 5 | contract TestIdentity is Identity { 6 | bytes private constant rsaExponent = hex"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001"; 7 | bytes private constant rsaModulus = hex"B0CA0A0EEBA26CDA6C0FA56527056D49C91045CFD6598A050F7CA831173416426F3630264221C94B8189F19CBFA2073682CE16A459C5E59978320FFC9AF811A8E9B1264C4DD3B32339791458EFD65776229FF82CE26389BACFB606FDFD05AD0253045931B7BFD1383CA5D2E5F49796C828A59321F1632CED38FE66FD668D9C2851F47E50CF337A1FE37C189DA93E2CFC5CFCB833C06F749FF405E33C94AEA36D120B622E9C61BEADDF677458EEA985C6567C7A54B3F6D079FBFB6C563548C0349E5D08972D9E6BE932E69001AB0390363A342A9A874C70B231EB0B0CD90D4599D914C7ADF755EE5E5012099AA1DC5A7152B598B8F90C2B16842A26F650FE5E6F"; 8 | 9 | constructor(string memory sub, string memory aud) 10 | Identity(sub, aud, JWKS(0)) 11 | public payable { } 12 | 13 | function getRsaExponent(string memory) internal pure returns (bytes memory) { 14 | return rsaExponent; 15 | } 16 | 17 | function getRsaModulus(string memory) internal view returns (bytes memory) { 18 | return rsaModulus; 19 | } 20 | } -------------------------------------------------------------------------------- /migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/solidity-jwt/6ce4f3849005ded461b4c534c907b6d685feaac7/migrations/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-jwt", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "test": "scripts/test.sh \"$@\"" 10 | }, 11 | "dependencies": { 12 | "core-js": "^2.6.5", 13 | "vue": "^2.6.6", 14 | "web3": "1.0.0-beta.37" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "^3.5.0", 18 | "@vue/cli-plugin-eslint": "^3.5.0", 19 | "@vue/cli-service": "^3.5.0", 20 | "babel-eslint": "^10.0.1", 21 | "chai": "^4.2.0", 22 | "eslint": "^5.8.0", 23 | "eslint-plugin-vue": "^5.0.0", 24 | "ganache-cli": "^6.4.3", 25 | "truffle": "^5.0.11", 26 | "truffle-hdwallet-provider": "^1.0.17", 27 | "vue-template-compiler": "^2.5.21" 28 | }, 29 | "eslintConfig": { 30 | "root": true, 31 | "env": { 32 | "node": true 33 | }, 34 | "extends": [ 35 | "plugin:vue/essential", 36 | "eslint:recommended" 37 | ], 38 | "rules": { 39 | "no-console": "off" 40 | }, 41 | "parserOptions": { 42 | "parser": "babel-eslint" 43 | } 44 | }, 45 | "postcss": { 46 | "plugins": { 47 | "autoprefixer": {} 48 | } 49 | }, 50 | "browserslist": [ 51 | "> 1%", 52 | "last 2 versions", 53 | "not ie <= 8" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAsMoKDuuibNpsD6VlJwVtSckQRc/WWYoFD3yoMRc0FkJvNjAm 3 | QiHJS4GJ8Zy/ogc2gs4WpFnF5Zl4Mg/8mvgRqOmxJkxN07MjOXkUWO/WV3Yin/gs 4 | 4mOJus+2Bv39Ba0CUwRZMbe/0Tg8pdLl9JeWyCilkyHxYyztOP5m/WaNnChR9H5Q 5 | zzN6H+N8GJ2pPiz8XPy4M8BvdJ/0BeM8lK6jbRILYi6cYb6t32d0WO6phcZWfHpU 6 | s/bQefv7bFY1SMA0nl0Ily2ea+ky5pABqwOQNjo0KpqHTHCyMesLDNkNRZnZFMet 7 | 91XuXlASCZqh3FpxUrWYuPkMKxaEKib2UP5ebwIDAQABAoIBAQCG5iQ2rmcKWMEM 8 | T1CYLfPu0ZWxL56IRGEIKD7O9r2wHpFt79/BprZK/1WbH9i6cnaN6WQtlLMFN84X 9 | pUtDlPckxO2AI8lfONowCMwV8SzFIEfbeSwGs51tBwIG49gmdJXJcw/8eBHOO/NL 10 | NVpzARj1Ms7lLola+/UoADfmNFLqTDPY1ROcDbxWZr0AaYwXjWcSpsndTRTmjUP4 11 | Avr/coqaLJXjqLlIIRIgELwHphQlcBVp5WYkiPXtwAvsUgtC9ZMcFMnfZpmRj0i5 12 | WJ6dgs83IAHZNbIEzbFYnllN6y4DqyaCNUDA61rIJuqhmXi0x+kjo8KeSjwUwgs4 13 | lvRv530BAoGBAOoDJvpt2/uQIt0Df5OZEPuFew6saBqa+Vs4kOcf5jfNJLyeVTCX 14 | PWNe5sRNIy7z7hvWc+Jhzun3XdOQ1Fkv7ToNNCgX+En7fLyHMWS/QhWJspB5l6H2 15 | 7ay8bRGsOtQt1EHfdO8BBpIsibmi4KRSqdRNJsV9BA+8WU3I4nxdSsyRAoGBAMFm 16 | dppl6/LZIBEE8sYRGJH7BioaATZSP2GcWL+DX/qHMfx9AcafFRSZ9goPTli5Yk88 17 | 7JrQYQSTn7QPSGd6FZQ3RHL3SApk/kkymb/8lJHs9B7AFaLQFkIpHY9TvFg5LxNa 18 | CgnVm62sgOo73kNp0zBQJmcbSs/2WHeQecWjx/r/AoGAPIxWQIO+YIe5zY/DKbB4 19 | KWwvG+vV/9RLYqPvMi4ZxiRpNfiNuEXZl2WNtgrzaImq+T7yuwdtnzHm7//QQcdZ 20 | zSRvc1RIZPQSIUGlsRifg60eK62aY0jd1Ezot6LlkdLN0cF8ov6T8axeck7Hax0M 21 | XiccOgsPyvaD8NFicKU+3lECgYEAomom/iDL1mSq/j/2lYlz5dw2YBL4o6LMoW+I 22 | RETSzHTO8KgWXrPoIyXsrqQN+oqs2bhjRtvqLcq/mpofBu9Kwe7r59RsxnXr6pK4 23 | 9bbrGeUxWcWcJd+0YZlfvqbmi/xx4UGDHqEpnN86X/so+9oaRf95UKtl12GdB8Pn 24 | z6dsYQcCgYEAlRq7GvUsi2Qyam96GMYJ9jt91lkyn2ZGCJopTwu4QeqUTUVsey9X 25 | YYa+vwsJx5MOPmv596RQ21fIr4srpitxOzeryF/quPXBmVS9PUmu1AnsrELxdyt9 26 | 4okJg1XtZsGH5HN8huYWZ6iq7TxgD4PN2mZV5WOt9p6f++yN1zlULW8= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMoKDuuibNpsD6VlJwVt 3 | SckQRc/WWYoFD3yoMRc0FkJvNjAmQiHJS4GJ8Zy/ogc2gs4WpFnF5Zl4Mg/8mvgR 4 | qOmxJkxN07MjOXkUWO/WV3Yin/gs4mOJus+2Bv39Ba0CUwRZMbe/0Tg8pdLl9JeW 5 | yCilkyHxYyztOP5m/WaNnChR9H5QzzN6H+N8GJ2pPiz8XPy4M8BvdJ/0BeM8lK6j 6 | bRILYi6cYb6t32d0WO6phcZWfHpUs/bQefv7bFY1SMA0nl0Ily2ea+ky5pABqwOQ 7 | Njo0KpqHTHCyMesLDNkNRZnZFMet91XuXlASCZqh3FpxUrWYuPkMKxaEKib2UP5e 8 | bwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/solidity-jwt/6ce4f3849005ded461b4c534c907b6d685feaac7/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | OpenZeppelin Identity Recovery Demo 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /scripts/deployKeys.js: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const JWKS = artifacts.require("JWKS"); 3 | const jwks = await JWKS.new(); 4 | await jwks.addKey( 5 | "3db3ed6b9574ee3fcd9f149e59ff0eef4f932153", 6 | "0xd8a729bf80b14e8782284217ce786a3e0db53210803fd0e75f9b36fd4759d5bcce56147caa2a24fcff23ef2f1817633625cf2bd9bb5f0a02461658db92db557385ef9de3de3d3fa119ff4f7a423545487bca4e8f786d240899e6716620617c572fc3f44c33479379964f80e5c8dd8209c968c067d154b25b7b5a82d4d0764573f2723d117c3369229e4758c67cc0f8c8f309eb5796a9a102bfb02cf83f40b2b0002c91205d8524781f3ecbea69e17b257a34cc73dc1ae1d43aa5c21e89fa2a21d917b382e1bcd3b93133562a494cb632f505322f83362fc6d0bb5212512697863fa2d564f4443270aa98a8385a6b545aaa915bdb516d275c3ff1d540389ef7fb" 7 | ); 8 | await jwks.addKey( 9 | "8a63fe71e53067524cbbc6a3a58463b3864c0787", 10 | "0xc9b3cb7ce8a86e462a3c97f64bdf1390fd1876fcf24aa3d200d6b5470f1012d4f6231ab67eed4314e9fdf2b7b5aa3627e4740f956e87be7fffc3d26694677e98a83f5c9bef11af354e6fad3fdb53ae07e5022ce36d31df5fdfa7f4d16529aff56e52781ca627d6f9219b08423e6bb25de6fbb07641227bef8e5e25695077555c2282a82b799045eb96a874c715908ab307ee95cbf58a791a8047eb0d7097fcb1d48dbce4b03cf43f830dcc437f1289e9b155591f9e7e805a2721b8423ded2dbae08bb380d245e538a9a533e3ce326ffaac62b110ea326bda7a48b53c27bc098f4429027105664ecba5a56ddcb5826cce78bb171152f922c1722c65fa4ead7699" 11 | ); 12 | console.error("JWKS deployed") 13 | console.log(jwks.address); 14 | } 15 | 16 | module.exports = function(cb) { 17 | main().then(() => cb()).catch(err => cb(err)); 18 | } -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | # Exit script as soon as a command fails. 2 | set -o errexit 3 | 4 | # Executes cleanup function at script exit. 5 | trap cleanup EXIT 6 | 7 | ganache_port=9555 8 | 9 | cleanup() { 10 | # Kill the ganache instance that we started (if we started one and if it's still running). 11 | if [ -n "$ganache_pid" ] && ps -p $ganache_pid > /dev/null; then 12 | kill -9 $ganache_pid 13 | fi 14 | } 15 | 16 | ganache_running() { 17 | nc -z localhost "$ganache_port" 18 | } 19 | 20 | start_ganache() { 21 | node_modules/.bin/ganache-cli --networkId 4447 -p $ganache_port -d -u 0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef > /dev/null & 22 | ganache_pid=$! 23 | } 24 | 25 | if ganache_running; then 26 | echo "Using existing ganache instance" 27 | else 28 | echo "Starting our own ganache instance" 29 | start_ganache 30 | fi 31 | 32 | if [ "$CI" = true ]; then 33 | node_modules/.bin/truffle version 34 | node_modules/.bin/truffle compile 35 | fi 36 | 37 | node_modules/.bin/truffle test --network testing "$@" 38 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 59 | 60 | 83 | -------------------------------------------------------------------------------- /src/components/CreateIdentity.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 55 | 56 | 64 | -------------------------------------------------------------------------------- /src/components/GoogleLogin.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 59 | 60 | 67 | -------------------------------------------------------------------------------- /src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 34 | -------------------------------------------------------------------------------- /src/components/Recover.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 103 | 104 | 128 | -------------------------------------------------------------------------------- /src/components/SignUp.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 42 | 43 | 53 | -------------------------------------------------------------------------------- /src/components/YourAddress.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | // import Home from './components/Home.vue' 4 | // import SignUp from './components/SignUp.vue' 5 | // import Recover from './components/Recover.vue' 6 | 7 | // Vue.config.productionTip = false 8 | 9 | // const routes = { 10 | // '/': Home, 11 | // '/signup': SignUp, 12 | // '/recover': Recover 13 | // } 14 | 15 | // new Vue({ 16 | // el: '#app', 17 | // data: { 18 | // currentRoute: window.location.pathname 19 | // }, 20 | // computed: { 21 | // ViewComponent () { 22 | // return routes[this.currentRoute] || { template: '

Not Found

' } 23 | // } 24 | // }, 25 | // render (h) { return h(this.ViewComponent) } 26 | // }); 27 | 28 | new Vue({ 29 | el: '#app', 30 | render: h => h(App) 31 | }) 32 | -------------------------------------------------------------------------------- /src/utils/contracts.js: -------------------------------------------------------------------------------- 1 | const IdentityArtifact = require('../../build/contracts/Identity.json'); 2 | 3 | export function Identity(address) { 4 | const { abi, bytecode } = IdentityArtifact; 5 | return new window.web3.eth.Contract(abi, address, { data: bytecode }) 6 | } -------------------------------------------------------------------------------- /src/utils/jwt.js: -------------------------------------------------------------------------------- 1 | export function splitToken(token) { 2 | return token.split('.'); 3 | } 4 | 5 | export function parseToken(token) { 6 | const [header, payload, signature] = splitToken(token); 7 | return { 8 | header: JSON.parse(atob(header)), 9 | payload: JSON.parse(atob(payload)), 10 | signature: '0x' + Buffer.from(signature, 'base64').toString('hex') 11 | } 12 | } 13 | 14 | export function tokenForRecovery(token) { 15 | const [header, payload, signature] = splitToken(token); 16 | return { 17 | header: atob(header), 18 | payload: atob(payload), 19 | signature: '0x' + Buffer.from(signature, 'base64').toString('hex') 20 | } 21 | } -------------------------------------------------------------------------------- /test/identity.test.js: -------------------------------------------------------------------------------- 1 | const TestIdentity = artifacts.require("TestIdentity"); 2 | require('chai').should(); 3 | 4 | contract.only("Identity", function([funder, owner]) { 5 | 6 | const device = '0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef'; 7 | 8 | before('fund device', async function () { 9 | await web3.eth.sendTransaction({ from: funder, to: device, value: 1e18 }); 10 | }); 11 | 12 | before('deploy', async function () { 13 | this.instance = await TestIdentity.new("1234567890", "theaudience.zeppelin.solutions", { from: owner }); 14 | }); 15 | 16 | it('works', async function () { 17 | const header = '{"alg":"RS256","typ":"JWT"}'; 18 | const payload = '{"sub":"1234567890","name":"John Doe","iat":1516239022,"nonce":"xf30B2uPOlNXxeOVq5cLW1QJj-8","aud":"theaudience.zeppelin.solutions"}'; 19 | 20 | const signatureBase64 = 'a2QWRyWWjx9cI1Ll9HlDkpxdJwxVha-r4UR1JmmdgbkgHqltWZsvlPZHiCp2GQbEqYDcSloorkwoWZFrSNDV9YNMrMOKl-c3Q9bylb8Y04MpcXlkU-h5DjyM5rLv6wzegP81x19suFRlWSim7U-0XsNcYwSPZmsEBoEQpBWhaWGXuF2NJ4XUUMcd4mNSiYq107-AfZWWuAHPXeJiNtcIAaVnb9m-qwwV2456qkdFRy7hwEgvoKJhgGRdzMYwXRkbP-oQG58jB_o_8ntwcdxWDEGSKCu1xIIQvIZJarpVgUq97phtDUHgIT4Q-Vc8A1k0Lu19HTy4ewoDiYJvwRP1Sw'; 21 | const signature = '0x' + Buffer.from(signatureBase64, 'base64').toString('hex'); 22 | 23 | const wasDeviceRegistered = await this.instance.accounts(device); 24 | wasDeviceRegistered.should.be.false; 25 | 26 | const tx = await this.instance.recover(header, payload, signature, { from: device, gas: 6e6 }); 27 | console.log(" gas:", tx.receipt.gasUsed); 28 | const deviceRegistered = await this.instance.accounts(device); 29 | deviceRegistered.should.be.true; 30 | }); 31 | 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /test/jwt.test.js: -------------------------------------------------------------------------------- 1 | const JWTContract = artifacts.require("JWT"); 2 | require('chai').should(); 3 | 4 | contract.skip("JWT", function([_, owner]) { 5 | 6 | before('deploy', async function () { 7 | this.instance = await JWTContract.new(); 8 | }) 9 | 10 | it('gets sub from jwt json', async function () { 11 | const actual = await this.instance.getSub('{ "name": "John Doe", "iat": 1516239022, "sub": "45642546" }'); 12 | actual.should.eq('45642546'); 13 | }); 14 | 15 | }); -------------------------------------------------------------------------------- /test/rsaverify.test.js: -------------------------------------------------------------------------------- 1 | const RSAContract = artifacts.require("SolRsaVerify"); 2 | require('chai').should(); 3 | 4 | contract.skip("RSA verify", function([_, owner]) { 5 | 6 | before('deploy', async function () { 7 | this.instance = await RSAContract.new(); 8 | }) 9 | 10 | it('verifies mock signature with 1024 bytes key', async function () { 11 | const message = web3.utils.asciiToHex("hello world"); 12 | const modulus = "0xB793F2F926170FAD768F8B1A5769A2243B4CDCAC4780194F59B39E1A2ABC3BB8EA42DB495D17BEC7F7072A11ED4FA510E75A7886A5DB6F71B7AFCA0090CA079889D18AF0669829ED29A8E21D0C09BD19CAAF2FE2CC8121BFC5687AC6698E3022F468A481426486CAD263BE1A119491E034A6E1AB78F19C066D4145A50F9ECFF7"; 13 | const exponent= "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001"; 14 | const signature = "0x57a0d6a185924d9d579b3ab319fe512331cb0bc6ef2da7d5285cbd06844f5c44662cae2e41ee5020893d6690e34b50a369a78250ae81ba6d708560535ef7cff0299f2ba070b096a9a76e84cf9c902b5e367b341ee166f5fc325dd08a3d971d96d528937f617a1eaf2250c56c4edca80c65970d54fe2492a19468bd32166b3c32"; 15 | 16 | const result = await this.instance.pkcs1Sha256VerifyRaw(message, signature, exponent, modulus); 17 | result.toNumber().should.be.eq(0); 18 | }); 19 | 20 | it('verifies mock signature with 2048 bytes key', async function () { 21 | const message = web3.utils.asciiToHex("hello world"); 22 | const modulus = "0xC93E1BD98562158C2DFCB14F2151C49CFCFEFD5C69F3B19470ED23BCA39B069EAAF28DD346A9BB43C37F867FF64E93D0843FBF61B54EEBC7F02984FD7216B047F5FF10DE088DF08934C1273001AD5C5E6D078161036D80484E25461C8067F9C8CF63B8539F2D4B1A8B7125BB02D5DC8933D3F361B008F2C71EA62F56CA83085FFC2CAF37A49004D6A933DB67B1F6F7D70AECE6C4788305D45D8C04BFFDD1DE4C534583DE1D4419F9D8BD92BA1DF397AE6C942D922C6732CAABC5C8556F3271F6A07FD63AE9AE83756D18DED8DC161535AFEDE0CD7A88E8C68A6A0A09E36E6432A97B04E1CCC5B34AFC18946790E18A4371CE0690A6D4AEE5A1D27C131E67D577"; 23 | const exponent= "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001"; 24 | const signature = "0x78321c8c54df34965435f2c4cbd087097abb925d0615793aeb86c82a66ead651c0f7eef0ed52a4a36aa14ba1165c394368d74870480b12f8746f9b67a887ecc254f9741ccd579366ae8a531fc88095c42aaf2d678551c75c82700167304cc67870b429239d2af6bcc5b881b89a18d585218edad3baf2b53d712c10f1eadf4249af0909efdd7b7b927e139397838e22a8efd180831c5fdbbcf3bf10383de5877df5227976892a2aaf655361f9825483902a11afc9e962d67344268cd6e0997f6e9b04cab7644de104df6428a614f75f73278e193bc2721e4f1a21b26bbaeeb624d2634246ca6f9dd04fddb34c4f492803cbe122df47b4b54a133215eddd6b5ce6"; 25 | 26 | const result = await this.instance.pkcs1Sha256VerifyRaw(message, signature, exponent, modulus); 27 | result.toNumber().should.be.eq(0); 28 | }); 29 | }); 30 | 31 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | local: { 4 | host: 'localhost', 5 | port: 8545, 6 | network_id: '*', 7 | gas: 5000000 8 | }, 9 | testing: { 10 | host: 'localhost', 11 | port: 9555, 12 | network_id: '4447', 13 | gas: 6000000 14 | }, 15 | "rinkeby-infura": { 16 | provider: () => new (require("truffle-hdwallet-provider"))(process.env.MNEMONIC, "https://rinkeby.infura.io/v3/" + process.env.INFURA_ID), 17 | network_id: 4, 18 | gas: 5000000, 19 | gasPrice: 10e9 20 | }, 21 | 22 | }, 23 | compilers: { 24 | solc: { 25 | version: "0.5.4", 26 | evmVersion: "constantinople" 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configureWebpack: { 3 | externals: { 4 | gapi: 'gapi' 5 | } 6 | } 7 | } 8 | --------------------------------------------------------------------------------