Add pgp signer.

This commit is contained in:
Spencer Ofwiti 2021-02-17 13:00:38 +03:00
parent 9d2fdd687b
commit 7165031fc8
7 changed files with 162 additions and 20 deletions

View File

@ -34,6 +34,7 @@
"mocha": "^8.2.1",
"moolb": "^0.1.0",
"ng2-charts": "^2.4.2",
"openpgp": "^4.10.10",
"popper.js": "^1.16.1",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",

View File

@ -3,3 +3,5 @@ export * from './array-sum';
export * from './accountIndex';
export * from './custom-error-state-matcher';
export * from './http-getter';
export * from './pgp-signer';

View File

@ -0,0 +1,8 @@
import { PGPSigner } from './pgp-signer';
let keystore;
describe('PgpSigner', () => {
it('should create an instance', () => {
expect(new PGPSigner(keystore)).toBeTruthy();
});
});

View File

@ -0,0 +1,102 @@
const openpgp = require('openpgp');
interface Signable {
digest(): string;
}
type Signature = {
engine: string
algo: string
data: string
digest: string;
};
interface Signer {
onsign(signature: Signature): void;
onverify(flag: boolean): void;
fingerprint(): string;
prepare(material: Signable): boolean;
verify(digest: string, signature: Signature): void;
sign(digest: string): void;
}
class PGPSigner implements Signer {
engine = 'pgp';
algo = 'sha256';
dgst: string;
signature: Signature;
keyStore;
onsign: (signature: Signature) => void;
onverify: (flag: boolean) => void;
constructor(keyStore) {
this.keyStore = keyStore;
this.onsign = (signature: Signature) => {};
this.onverify = (flag: boolean) => {};
}
public fingerprint(): string {
return this.keyStore.getFingerprint();
}
public prepare(material: Signable): boolean {
this.dgst = material.digest();
return true;
}
public verify(digest: string, signature: Signature): void {
openpgp.signature.readArmored(signature.data).then((sig) => {
const opts = {
message: openpgp.cleartext.fromText(digest),
publicKeys: this.keyStore.getTrustedKeys(),
signature: sig,
};
openpgp.verify(opts).then((v) => {
let i = 0;
for (i = 0; i < v.signatures.length; i++) {
const s = v.signatures[i];
if (s.valid) {
this.onverify(s);
return;
}
}
console.error('checked ' + i + ' signature(s) but none valid');
this.onverify(false);
});
}).catch((e) => {
console.error(e);
this.onverify(false);
});
}
public sign(digest: string): void {
const m = openpgp.cleartext.fromText(digest);
const pk = this.keyStore.getPrivateKey();
const opts = {
message: m,
privateKeys: [pk],
detached: true,
};
openpgp.sign(opts).then((s) => {
this.signature = {
engine: this.engine,
algo: this.algo,
data: s.signature,
// TODO: fix for browser later
digest,
};
this.onsign(this.signature);
}).catch((e) => {
console.error(e);
this.onsign(undefined);
});
}
}
export {
Signable,
Signature,
Signer,
PGPSigner
};

View File

@ -3,12 +3,17 @@ import {BehaviorSubject, Observable, of} from 'rxjs';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {environment} from '../../environments/environment';
import {first, map} from 'rxjs/operators';
import { Envelope, Syncable } from '../../assets/js/cic-meta/sync.js';
import { ArgPair, Envelope, Syncable } from '../../assets/js/cic-meta/sync.js';
import {PGPSigner, Signer} from '../_helpers';
@Injectable({
providedIn: 'root'
})
export class UserService {
keystore = '';
syncableAccount: Syncable;
signer: Signer = new PGPSigner(this.keystore);
accounts: any = '';
private accountsList = new BehaviorSubject<any>(this.accounts);
accountsSubject = this.accountsList.asObservable();
@ -48,9 +53,9 @@ export class UserService {
);
}
changeAccountInfo(address: string, status: string, name: string, phoneNumber: string, type: string, token: string,
failedPinAttempts: string, bio: string, gender: string, businessCategory: string, userLocation: string,
location: string, referrer: string): Observable<any> {
async changeAccountInfo(address: string, status: string, name: string, phoneNumber: string, type: string, token: string,
failedPinAttempts: string, bio: string, gender: string, businessCategory: string, userLocation: string,
location: string, referrer: string): Promise<any> {
const accountDetails = {
status,
name,
@ -65,20 +70,32 @@ export class UserService {
location,
referrer
};
const addressStub = address.slice(2);
const headers = new HttpHeaders().append('x-cic-automerge', 'client');
this.http.get(`${environment.cicMetaUrl}/${addressStub}`, { headers }).pipe(first()).subscribe(res => {
console.log(res);
const response = Envelope.fromJSON(res);
response.unwrap();
console.log(response);
}, error => {
let addressStub = address.slice(2);
addressStub = '0000000000000000000000000000000000000000000000000000000000000000';
const headers = new HttpHeaders({'x-cic-automerge': 'client'});
this.http.get(`${environment.cicMetaUrl}/${addressStub}`, { headers }).pipe(first()).subscribe(async res => {
this.syncableAccount = Envelope.fromJSON(res).unwrap();
let update = [];
for (const prop in accountDetails) {
update.push(new ArgPair(prop, accountDetails[prop]));
}
this.syncableAccount.update(update, 'client-branch');
await this.updateMeta(addressStub, headers);
}, async error => {
console.error('There is an error!', error);
const refName = addressStub + ':cic-person';
this.syncableAccount = new Syncable(refName, accountDetails);
await this.updateMeta(addressStub, headers);
});
this.http.put(`${environment.cicMetaUrl}/${addressStub}`, { data: accountDetails }, { headers }).pipe(first()).subscribe(res => {
return addressStub;
}
async updateMeta(addressStub: string, headers: HttpHeaders): Promise<any> {
const envelope = await this.wrap(this.syncableAccount , this.signer);
const reqBody = envelope.toJSON();
this.http.put(`${environment.cicMetaUrl}/${addressStub}`, { data: reqBody }, { headers }).pipe(first()).subscribe(res => {
console.log(res);
});
return of(addressStub);
}
getAccounts(): void {
@ -136,4 +153,18 @@ export class UserService {
return response;
}));
}
wrap(syncable: Syncable, signer: Signer): Promise<Envelope> {
return new Promise<Envelope>((whohoo, doh) => {
syncable.setSigner(signer);
syncable.onwrap = async (env) => {
if (env === undefined) {
doh();
return;
}
whohoo(env);
};
syncable.sign();
});
}
}

View File

@ -219,14 +219,14 @@
</div>
<div class="col-md-6 col-lg-4">
<button mat-raised-button color="warn" type="button" class="btn btn-outline-danger">
<button mat-raised-button color="warn" type="button" class="btn btn-outline-danger mb-3">
Delete User
</button>
</div>
<div class="col-md-6 col-lg-4">
<button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary ml-2">
SAVE
<button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary">
SAVE DETAILS
</button>
</div>
</div>

View File

@ -172,9 +172,7 @@ export class AccountDetailsComponent implements OnInit {
this.accountInfoFormStub.userLocation.value,
this.accountInfoFormStub.location.value,
this.accountInfoFormStub.referrer.value,
).pipe(first()).subscribe(res => {
console.log(res);
});
).then(res => console.log(res));
this.submitted = false;
}