+
+
+const StrappFileSystem = (() => {
+ /* Internal State */
+ let rootKey, rootPubKey
+ const rootDir = new StrappDirectory(StrappFile.literal({
+ type: 'strapp/directory',
+ perms: 0xFF4,
+ path: '.',
+ files: {},
+ loadFile(fileName, fileLiteral) {
+ this.files[fileName] = StrappFile.parse(fileLiteral, { path: fileName })
+ }
+ }))
+ const _strappRuntime = {
+ '.': rootDir,
+ '/': rootDir,
+ run: new StrappDirectory({
+ perms: 0xFFF4,
+ files: {
+ keyboard: new StrappDevice(),
+ mouse: new StrappDevice(),
+ touch: new StrappDevice(),
+ client: new StrappDirectory({
+ perms: 0xFF0,
+ files: {
+ create: new StrappFile({
+ GET(){},
+ POST(){} //TODO: create client action
+ })
+ }
+ }),
+ relay: new StrappDirectory({
+ perms: 0x000,
+ files: {
+ create: new StrappFile({
+ GET(){},
+ POST(){} //TODO: create relay
+ })
+ }
+ }),
+ spc: new StrappDirectory({
+ perms: 0xF00,
+ files: {
+ create: new StrappFile({
+ GET(){},
+ POST(){} //TODO: spc creation (probably only internal)
+ })
+ }
+ })
+ }
+ })
+ }
+
+ /* Internal Functions */
+ const _request = (location, method, data) =>
+ rootDir.resolveRequest(method, rootPubKey, data, location.split('/'))
+
+ /* API Definition */
+ const StrappFileSystem = {
+ request: (location, method, data) =>
+ new Promise((resolve, reject) => StrappFileSystem.loadWaiters.push([
+ location, method, data, resolve, reject
+ ])),
+ get: (location) => StrappFileSystem.request(location, 'GET'),
+ set: (location, data) => StrappFileSystem.request(location, 'PUT', data)
+ }
+ StrappFileSystem.loadWaiters = []
+
+
+ /* Init sequence */
+ const _defaultFS = {
+ '.': {
+ acct: StrappDirectory.literal()
+ },
+ 'acct': {
+ local: StrappFile.literal()
+ }
+ }
+ const _algo = {
+ name: 'RSA-OAEP',
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+ hash: { name: 'SHA-1' }
+ }
+
+ const _loadRootKeys = () =>
+ Promise.all([
+ localforage.getItem('/keys/id_rsa'),
+ localforage.getItem('/keys/id_rsa.pub')
+ ])
+ .then((files) =>
+ files[0] === null || files[1] === null ?
+ window.crypto.subtle.generateKey(_algo, true, ['encrypt', 'decrypt'])
+ .then((keyPair) => Promise.all([
+ window.crypto.subtle.exportKey('jwk', keyPair.privateKey),
+ window.crypto.subtle.exportKey('jwk', keyPair.publicKey)
+ ]))
+ .then((jwks) => Promise.all([
+ localforage.setItem('/keys/id_rsa', jwks[0]),
+ localforage.setItem('/keys/id_rsa.pub', jwks[1])
+ ])) :
+ Promise.resolve(files))
+ .then((jwks) => {
+ let hashAlg = jwks[0].alg.replace(_algo.name, '')
+ if (hashAlg.length === 0)
+ hashAlg = 'SHA-1'
+ else
+ hashAlg = 'SHA' + hashAlg
+ if (jwks[0].alg.indexOf(_algo.name) !== 0 || hashAlg != _algo.hash.name) {
+ console.log('Secure hash algorithm updated, old keys deleted')
+ return Promise.all([localforage.removeItem('/keys/id_rsa'),
+ localforage.removeItem('/keys/id_rsa.pub')
+ ]).then(_loadRootKeys)
+ }
+ rootPubKey = jwks[1].n
+ return localforage.assignItem('acct/local', { pubKey: jwks[1].n }).then(
+ () => window.crypto.subtle.importKey('jwk', jwks[0], _algo, true, ['decrypt']))
+ .then((key) => Promise.resolve(rootKey = key))
+ })
+
+ const _init = () => localforage.getItem('.')
+ .then((data) =>
+ data === null ?
+ Promise.all(
+ Object.keys(_defaultFS)
+ .map((key) => localforage.setItem(key, _defaultFS[key])))
+ .then(() => localforage.getItem('.')) :
+ Promise.resolve(data))
+ .then((rootFiles) => {
+ rootDir.loadFiles(rootFiles)
+ Object.assign(rootDir.files, _strappRuntime)
+ _loadRootKeys().then((data) => {
+ StrappFileSystem.request = _request
+ StrappFileSystem.loadWaiters
+ .map((w) => _request(w[0], w[1], w[2]).then(w[3], w[4]))
+ delete StrappFileSystem.loadWaiters
+ })
+ })
+
+ _init()
+
+ //localforage.clear().then(_init)
+
+ return StrappFileSystem
+})()
+
+