Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue objects as custom facts #265

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions _examples/document_sign/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
};
exports.__esModule = true;
var self_sdk_1 = require("../../src/self-sdk");
var process_1 = require("process");
var fs_1 = require("fs");
var groups = {};
// Wait til the response is received
Expand All @@ -60,7 +61,7 @@ function setupSDK(appID, appSecret) {
return [4 /*yield*/, self_sdk_1["default"].build(appID, appSecret, "random", storageFolder, opts)];
case 1:
sdk = _a.sent();
return [4 /*yield*/, sdk.messaging().permitConnection("*")];
return [4 /*yield*/, sdk.start()];
case 2:
_a.sent();
return [2 /*return*/, sdk];
Expand All @@ -70,7 +71,7 @@ function setupSDK(appID, appSecret) {
}
function main() {
return __awaiter(this, void 0, void 0, function () {
var appID, appSecret, selfID, sdk, terms, docs, content, resp, i;
var appID, appSecret, selfID, sdk, terms, content, docs, resp, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
Expand All @@ -81,28 +82,27 @@ function main() {
case 1:
sdk = _a.sent();
terms = "please, read and accept terms and conditions";
docs = [];
content = fs_1.readFileSync("./sample.pdf").toString('utf8');
docs.push({
name: "Terms and conditions",
data: content,
mime: "application/pdf"
});
content = fs_1.readFileSync("./sample.pdf", null);
docs = [{
name: "Terms and conditions",
data: content,
mime: "application/pdf"
}];
return [4 /*yield*/, sdk.docs().requestSignature(selfID, terms, docs)];
case 2:
resp = _a.sent();
if (resp["status"] == "accepted") {
console.log("Document signed!");
console.log("");
console.log("signned documents: ");
for (i = 0; i < resp["signed_objects"].length; i++) {
console.log("- Name : " + resp["signed_objects"]["name"]);
console.log(" Link : " + resp["signed_objects"]["link"]);
console.log(" Hash : " + resp["signed_objects"]["hash"]);
console.log("- Name : " + resp["signed_objects"][0]["name"]);
console.log(" Link : " + resp["signed_objects"][0]["link"]);
console.log(" Hash : " + resp["signed_objects"][0]["hash"]);
}
console.log("");
console.log("full signature");
console.log(resp["input"]);
process_1.exit(0);
}
return [2 /*return*/];
}
Expand Down
3 changes: 1 addition & 2 deletions _examples/document_sign/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ async function main() {
const sdk = await setupSDK(appID, appSecret)
let terms = "please, read and accept terms and conditions"

var myArrayBuffer = readFileSync("./sample.pdf", null).buffer;
var content = new Uint8Array(myArrayBuffer);
var content = readFileSync("./sample.pdf", null);

let docs = [{
name: "Terms and conditions",
Expand Down
142 changes: 142 additions & 0 deletions _examples/fact_request/custom-objects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"use strict";
// Copyright 2020 Self Group Ltd. All Rights Reserved.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
exports.__esModule = true;
var self_sdk_1 = require("../../src/self-sdk");
var process_1 = require("process");
var facts_service_1 = require("../../src/facts-service");
var fs_1 = require("fs");
function delay(ms) {
return new Promise(function (resolve) { return setTimeout(resolve, ms); });
}
function request(appID, appSecret, selfID) {
return __awaiter(this, void 0, void 0, function () {
var opts, storageFolder, sdk, source, content, obj, fact, res, at, o, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
opts = { 'logLevel': 'debug' };
if (process.env["SELF_ENV"] != "") {
opts['env'] = process.env["SELF_ENV"];
}
storageFolder = __dirname.split("/").slice(0, -1).join("/") + "/.self_storage";
return [4 /*yield*/, self_sdk_1["default"].build(appID, appSecret, "random", storageFolder, opts)];
case 1:
sdk = _a.sent();
return [4 /*yield*/, sdk.start()];
case 2:
_a.sent();
source = "supu";
content = fs_1.readFileSync("./my_image.png");
return [4 /*yield*/, sdk.newObject("test", content, "image/png")
// obj.save("./my_image_copy.jpg")
];
case 3:
obj = _a.sent();
fact = new facts_service_1.FactToIssue("image", obj, source);
sdk.logger.info("fact created");
sdk.logger.info("issuing fact");
return [4 /*yield*/, sdk.facts().issue(selfID, [fact])];
case 4:
_a.sent();
return [4 /*yield*/, delay(10000)];
case 5:
_a.sent();
sdk.logger.info("sending a fact request (" + fact.key + ") to " + selfID);
sdk.logger.info("waiting for user input");
_a.label = 6;
case 6:
_a.trys.push([6, 14, , 15]);
return [4 /*yield*/, sdk.facts().request(selfID, [{
fact: fact.key,
issuers: [appID]
}])];
case 7:
res = _a.sent();
if (!!res) return [3 /*break*/, 8];
sdk.logger.warn("fact request has timed out");
return [3 /*break*/, 13];
case 8:
if (!(res.status === 'accepted')) return [3 /*break*/, 12];
at = res.attestation(fact.key);
if (!(at != null)) return [3 /*break*/, 10];
sdk.logger.info(selfID + " digest is \"" + at.value + "\"");
return [4 /*yield*/, res.object(at.value)];
case 9:
o = _a.sent();
o.save("./received.png");
sdk.logger.info("result stored at /tmp/received.png");
return [3 /*break*/, 11];
case 10:
sdk.logger.warn("No attestations have been returned");
_a.label = 11;
case 11: return [3 /*break*/, 13];
case 12:
sdk.logger.warn(selfID + " has rejected your authentication request");
_a.label = 13;
case 13: return [3 /*break*/, 15];
case 14:
error_1 = _a.sent();
sdk.logger.error(error_1.toString());
return [3 /*break*/, 15];
case 15:
sdk.close();
process_1.exit();
return [2 /*return*/];
}
});
});
}
function main() {
return __awaiter(this, void 0, void 0, function () {
var appID, appSecret, selfID;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
appID = process.env["SELF_APP_ID"];
appSecret = process.env["SELF_APP_SECRET"];
selfID = process.env["SELF_USER_ID"];
return [4 /*yield*/, request(appID, appSecret, selfID)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
main();
79 changes: 79 additions & 0 deletions _examples/fact_request/custom-objects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2020 Self Group Ltd. All Rights Reserved.

import SelfSDK from '../../src/self-sdk'
import { exit } from 'process';
import { FactToIssue, Group } from '../../src/facts-service';
import { readFileSync } from 'fs';
import * as fs from 'fs';
import { writeFileSync } from 'fs';

function delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}

async function request(appID: string, appSecret: string, selfID: string) {
// const SelfSDK = require("self-sdk");
let opts = {'logLevel': 'debug'}
if (process.env["SELF_ENV"] != "") {
opts['env'] = process.env["SELF_ENV"]
}
let storageFolder = __dirname.split("/").slice(0,-1).join("/") + "/.self_storage"
const sdk = await SelfSDK.build( appID, appSecret, "random", storageFolder, opts);
await sdk.start()

let source = "supu"
let content = readFileSync("./my_image.png");
let obj = await sdk.newObject("test", content, "image/png")
// obj.save("./my_image_copy.jpg")

let fact = new FactToIssue("image", obj, source)
sdk.logger.info("fact created")

sdk.logger.info("issuing fact")
await sdk.facts().issue(selfID, [fact])

await delay(10000);
sdk.logger.info(`sending a fact request (${fact.key}) to ${selfID}`)
sdk.logger.info(`waiting for user input`)

try {
let res = await sdk.facts().request(selfID, [{
fact: fact.key,
issuers: [ appID ]
}])

if (!res) {
sdk.logger.warn(`fact request has timed out`)
} else if (res.status === 'accepted') {
let at = res.attestation(fact.key)
if (at != null) {
sdk.logger.info(`${selfID} digest is "${at.value}"`)
const o = await res.object(at.value)
o.save("./received.png")
sdk.logger.info(`result stored at /tmp/received.png`)
} else {
sdk.logger.warn(`No attestations have been returned`)
}
} else {
sdk.logger.warn(`${selfID} has rejected your authentication request`)
}
} catch (error) {
sdk.logger.error(error.toString())
}

sdk.close()
exit();
}

async function main() {
let appID = process.env["SELF_APP_ID"]
let appSecret = process.env["SELF_APP_SECRET"]
let selfID = process.env["SELF_USER_ID"]

await request(appID, appSecret, selfID);
}

main();



29 changes: 28 additions & 1 deletion src/chat-object.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright 2020 Self Group Ltd. All Rights Reserved.
import { logging, Logger } from './logging'
import { createHash } from 'crypto';
import { writeFileSync } from 'fs';

/**
* FileObject represents an object (image or file) shared through messaging.
Expand All @@ -19,6 +21,7 @@ export class FileObject {
_sodium: any
logger: Logger
fi: RemoteFileInteractor
hash: string

/**
* Creates a new FileObject.
Expand All @@ -44,7 +47,7 @@ export class FileObject {
* @param mime mime type.
* @returns the current FileObject
*/
async buildFromData(name: string, data: string|Uint8Array, mime: string): Promise<FileObject> {
async buildFromData(name: string, data: Buffer, mime: string): Promise<FileObject> {
await this._sodium.ready;

// Encrypt the message
Expand All @@ -59,6 +62,8 @@ export class FileObject {
this.mime = mime
this.name = name
this.public = false
this.content = data
this.hash = this.calculateHash(data)

return this
}
Expand Down Expand Up @@ -87,6 +92,8 @@ export class FileObject {
}

this.content = buf

this.hash = this.calculateHash(this.content.toString('binary'))
this.link = input['link']
this.name = input['name']
this.mime = input['mime']
Expand All @@ -108,9 +115,15 @@ export class FileObject {
mime: this.mime,
expires: this.expires,
public: this.public,
object_hash: this.hash
}
}

save(path: string) {
// let newContent = Buffer.from(this.content, 'binary');
writeFileSync(path, this.content);
}

private encryptObject(plaintext: string|Uint8Array) {
const key = this._sodium.crypto_aead_xchacha20poly1305_ietf_keygen();
const pNonce = this._sodium.randombytes_buf(this._sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
Expand All @@ -125,6 +138,20 @@ export class FileObject {
return { key: this.buildShareableKey(key, pNonce), ciphertext: ct }
}

/**
* Calculates the sha256 hash for a given string.
* @param ct input to be hashed
* @returns the hash of the input.
*/
private calculateHash(ct: string|Uint8Array): string {
const hash = createHash('sha256');
hash.update(ct);
const base64Url = hash.digest().toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
const urlSafeBase64Url = base64Url.replace(/=+$/, '');

return urlSafeBase64Url;
}

/**
* Extracts key and nonce from a shareable key
* @param key shareable key
Expand Down
2 changes: 1 addition & 1 deletion src/docs-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { FileObject } from './chat-object';

interface Doc {
name: string,
data: string|Uint8Array,
data: Buffer,
mime: string,
}
export default class DocsService {
Expand Down
2 changes: 1 addition & 1 deletion src/fact-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export default class FactResponse {

async object(hash: string): Promise<FileObject> {
for (const o of this.payload.objects) {
if (o.image_hash == hash) {
if (o.image_hash == hash || o.object_hash == hash) {
const fo = new FileObject(this.is.jwt.authToken(), this.is.url)
return fo.buildFromObject(o)
}
Expand Down
Loading
Loading