├── .gitmodules ├── .gitignore ├── debian ├── postgresql-pgcrypto-openpgp │ ├── control-9.1 │ ├── control-9.2 │ ├── control-9.3 │ ├── patches │ │ ├── 9 │ │ │ └── 101-use-pgxs-by-default.patch │ │ ├── 9.1.14 │ │ │ ├── 020-pgp-pgsql-includes.patch │ │ │ └── 200-pgp-pgsql-includes.patch │ │ ├── 9.1 │ │ │ └── 050-randombytes.patch │ │ └── 9.1.13 │ │ │ └── 101-pxmemset.patch │ ├── control │ └── rules └── bankapi │ ├── postrm │ ├── changelog │ ├── control │ ├── postinst │ └── rules ├── doc ├── BankAPI Key Exchange.pdf ├── BankAPI Key Exchange.png ├── BankAPI System Design.pdf ├── BankAPI System Design.png ├── BankAPI Protocol Design.pdf ├── BankAPI Protocol Design.png ├── BankAPI Key Exchange.graffle ├── BankAPI System Design.graffle ├── BankAPI Protocol Design.graffle ├── call_graph │ ├── perfunction │ │ ├── 11910.svg │ │ ├── 3224943.svg │ │ ├── 3225049.svg │ │ ├── 3224946.svg │ │ ├── 3224947.svg │ │ ├── 3224994.svg │ │ ├── 3224995.svg │ │ ├── 3225046.svg │ │ ├── 3224977.svg │ │ ├── 3225048.svg │ │ ├── 3224989.svg │ │ ├── 3224992.svg │ │ ├── 3224986.svg │ │ ├── 3225052.svg │ │ ├── 3225047.svg │ │ ├── 3225051.svg │ │ └── 3225050.svg │ ├── tableusage │ │ ├── tlf3225046.svg │ │ ├── tlf3225047.svg │ │ ├── tlf3225049.svg │ │ ├── tlf3225051.svg │ │ ├── tlf3225048.svg │ │ ├── tlf3225050.svg │ │ ├── tlf3224943.svg │ │ ├── tlf3225052.svg │ │ ├── r3224908.svg │ │ ├── r3224895.svg │ │ ├── r3224921.svg │ │ ├── r3225023.svg │ │ ├── r3225032.svg │ │ └── r3225005.svg │ ├── t3225049.svg │ ├── t3225046.svg │ ├── t3225047.svg │ ├── t3225048.svg │ ├── t3225052.svg │ ├── t3225051.svg │ ├── index.html │ └── t3225050.svg ├── key_management.md └── rationale.md ├── misc ├── pg_hba.sql └── roles.sql ├── TABLES ├── files.sql ├── banks.sql ├── keys.sql └── messages.sql ├── testdata ├── index.sql ├── test-banks.sql ├── test-secretkeyring-TESTBANK001.sql ├── test-secretkeyring-TESTBANK002.sql └── test-publickeyrings.sql ├── etc └── apache2 │ └── sites-available │ └── 010-bankapi.conf ├── FUNCTIONS ├── register_bank.sql ├── get_bank.sql ├── get_message.sql ├── update_message_state.sql ├── encrypt_sign.sql ├── read_message.sql ├── decrypt_message.sql ├── register_secret_keyring.sql ├── decrypt_verify.sql ├── decode_delivery_receipt.sql ├── list_messages.sql ├── register_public_keyring.sql ├── create_message.sql ├── get_next_unprocessed_message.sql └── receive_message.sql ├── install.sh ├── Makefile ├── install.sql ├── LICENSE.md ├── demo-bankapi.sh ├── cgi └── bankapi.py ├── demo.sh ├── test.sql └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/control-9.1: -------------------------------------------------------------------------------- 1 | Depends: postgresql-9.1 (>= 9.1) 2 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/control-9.2: -------------------------------------------------------------------------------- 1 | Depends: postgresql-9.2 (>= 9.2) 2 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/control-9.3: -------------------------------------------------------------------------------- 1 | Depends: postgresql-9.3 (>= 9.3) 2 | -------------------------------------------------------------------------------- /doc/BankAPI Key Exchange.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI Key Exchange.pdf -------------------------------------------------------------------------------- /doc/BankAPI Key Exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI Key Exchange.png -------------------------------------------------------------------------------- /doc/BankAPI System Design.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI System Design.pdf -------------------------------------------------------------------------------- /doc/BankAPI System Design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI System Design.png -------------------------------------------------------------------------------- /debian/bankapi/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -f "/etc/apache2/sites-enabled/010-bankapi.conf" 5 | -------------------------------------------------------------------------------- /doc/BankAPI Protocol Design.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI Protocol Design.pdf -------------------------------------------------------------------------------- /doc/BankAPI Protocol Design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI Protocol Design.png -------------------------------------------------------------------------------- /doc/BankAPI Key Exchange.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI Key Exchange.graffle -------------------------------------------------------------------------------- /doc/BankAPI System Design.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI System Design.graffle -------------------------------------------------------------------------------- /doc/BankAPI Protocol Design.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/bankapi/master/doc/BankAPI Protocol Design.graffle -------------------------------------------------------------------------------- /misc/pg_hba.sql: -------------------------------------------------------------------------------- 1 | \echo Add the following line at the appropriate location in your pg_hba.conf: 2 | \echo local www-data bankapi peer 3 | -------------------------------------------------------------------------------- /debian/bankapi/changelog: -------------------------------------------------------------------------------- 1 | bankd (1.0) UNRELEASED; urgency=low 2 | 3 | * Initial release. 4 | 5 | -- per Tue, 05 Aug 2014 14:06:10 +0200 6 | -------------------------------------------------------------------------------- /misc/roles.sql: -------------------------------------------------------------------------------- 1 | CREATE USER "www-data" WITH NOCREATEDB NOCREATEUSER; 2 | 3 | GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "www-data"; 4 | -------------------------------------------------------------------------------- /TABLES/files.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Files ( 2 | FileID text not null, 3 | Plaintext text not null, 4 | Datestamp timestamptz not null default now(), 5 | PRIMARY KEY (FileID) 6 | ); 7 | -------------------------------------------------------------------------------- /testdata/index.sql: -------------------------------------------------------------------------------- 1 | \i testdata/test-banks.sql 2 | \i testdata/test-publickeyrings.sql 3 | \i testdata/test-secretkeyring-TESTBANK001.sql 4 | \i testdata/test-secretkeyring-TESTBANK002.sql 5 | -------------------------------------------------------------------------------- /etc/apache2/sites-available/010-bankapi.conf: -------------------------------------------------------------------------------- 1 | Alias /bankapi /var/www/bankapi/bankapi.py 2 | 3 | 4 | Options +ExecCGI 5 | 6 | SetHandler cgi-script 7 | 8 | 9 | -------------------------------------------------------------------------------- /TABLES/banks.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Banks ( 2 | BankID text not null, 3 | Protocol text not null, 4 | Host text not null, 5 | Port integer not null, 6 | Path text not null, 7 | Datestamp timestamptz not null default now(), 8 | PRIMARY KEY (BankID) 9 | ); 10 | -------------------------------------------------------------------------------- /FUNCTIONS/register_bank.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Register_Bank(_BankID text, _Protocol text, _Host text, _Port integer, _Path text) RETURNS BOOLEAN AS $BODY$ 2 | INSERT INTO Banks (BankID, Protocol, Host, Port, Path) VALUES ($1, $2, $3, $4, $5) RETURNING TRUE 3 | $BODY$ LANGUAGE sql VOLATILE; 4 | -------------------------------------------------------------------------------- /FUNCTIONS/get_bank.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Get_Bank(OUT Protocol text, OUT Host text, OUT Port integer, OUT Path text, _BankID text) RETURNS RECORD 2 | SET search_path TO public, pg_temp 3 | AS $BODY$ 4 | SELECT Banks.Protocol, Banks.Host, Banks.Port, Banks.Path FROM Banks WHERE Banks.BankID = $1 5 | $BODY$ LANGUAGE sql STABLE SECURITY DEFINER; 6 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/patches/9.1.14/020-pgp-pgsql-includes.patch: -------------------------------------------------------------------------------- 1 | --- a/pgp-pgsql.c 2014-09-03 10:49:50.981624594 +0200 2 | +++ b/pgp-pgsql.c 2014-09-03 10:50:16.977620859 +0200 3 | @@ -31,8 +31,6 @@ 4 | 5 | #include "postgres.h" 6 | 7 | -#include "fmgr.h" 8 | -#include "parser/scansup.h" 9 | #include "mb/pg_wchar.h" 10 | #include "utils/builtins.h" 11 | 12 | -------------------------------------------------------------------------------- /debian/bankapi/control: -------------------------------------------------------------------------------- 1 | Section: web 2 | Priority: extra 3 | Maintainer: Per Lejontand 4 | Homepage: https://github.com/trustly/bankapi 5 | Package: bankapi 6 | Architecture: all 7 | Depends: postgresql-pgcrypto-openpgp, apache2 (>= 2.2), python2.7 (>= 2.7), python-psycopg2 (>= 2.4), python-requests (>= 0.8) 8 | Description: Web API and database setup scripts for the bankapi backend. 9 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo apt-get -y build-dep postgresql postgresql-9.3 3 | sudo apt-get -y install postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3 postgresql-server-dev-9.3 4 | git clone https://github.com/johto/postgres.git 5 | cd postgres/contrib/pgcrypto 6 | git checkout pgcrypto_signatures 7 | USE_PGXS=1 make 8 | sudo env USE_PGXS=1 make install 9 | 10 | psql -X -f install.sql 11 | -------------------------------------------------------------------------------- /testdata/test-banks.sql: -------------------------------------------------------------------------------- 1 | SELECT Register_Bank( 2 | _BankID := 'TESTBANK001', 3 | _Protocol := 'https', 4 | _Host := 'www.testbank1.com', 5 | _Port := 443, 6 | _Path := '/api' 7 | ); 8 | 9 | SELECT Register_Bank( 10 | _BankID := 'TESTBANK002', 11 | _Protocol := 'https', 12 | _Host := 'www.testbank2.com', 13 | _Port := 443, 14 | _Path := '/api' 15 | ); 16 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/patches/9.1.14/200-pgp-pgsql-includes.patch: -------------------------------------------------------------------------------- 1 | --- a/pgp-pgsql.c 2014-09-03 10:52:13.169616479 +0200 2 | +++ b/pgp-pgsql.c 2014-09-03 10:52:54.917614814 +0200 3 | @@ -32,6 +32,8 @@ 4 | #include "postgres.h" 5 | 6 | #include "catalog/pg_type.h" 7 | +#include "fmgr.h" 8 | +#include "parser/scansup.h" 9 | #include "mb/pg_wchar.h" 10 | #include "utils/builtins.h" 11 | #include "utils/array.h" 12 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/patches/9/101-use-pgxs-by-default.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Makefile b/Makefile 2 | index 61dd575..ccf1168 100644 3 | --- a/Makefile 4 | +++ b/Makefile 5 | @@ -36,7 +36,7 @@ REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \ 6 | 7 | EXTRA_CLEAN = gen-rtab 8 | 9 | -ifdef USE_PGXS 10 | +ifndef NO_PGXS 11 | PG_CONFIG = pg_config 12 | PGXS := $(shell $(PG_CONFIG) --pgxs) 13 | include $(PGXS) 14 | -------------------------------------------------------------------------------- /FUNCTIONS/get_message.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Get_Message(OUT Ciphertext text, _MessageID text) RETURNS TEXT 2 | SET search_path TO public, pg_temp 3 | AS $BODY$ 4 | DECLARE 5 | BEGIN 6 | EXECUTE $SQL$SELECT armor(Cipherdata,ARRAY['Comment'],ARRAY[MessageType]) FROM Messages WHERE MessageID LIKE $1$SQL$ INTO STRICT Ciphertext USING (_MessageID || '%'); 7 | RETURN; 8 | END; 9 | $BODY$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; 10 | 11 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/patches/9.1/050-randombytes.patch: -------------------------------------------------------------------------------- 1 | --- a/pgcrypto--1.0.sql 2014-08-11 16:23:52.493699929 +0200 2 | +++ b/pgcrypto--1.0.sql 2014-08-11 16:24:05.936422558 +0200 3 | @@ -61,7 +61,7 @@ 4 | CREATE FUNCTION gen_random_bytes(int4) 5 | RETURNS bytea 6 | AS 'MODULE_PATHNAME', 'pg_random_bytes' 7 | -LANGUAGE 'C' VOLATILE STRICT; 8 | +LANGUAGE C VOLATILE STRICT; 9 | 10 | -- 11 | -- pgp_sym_encrypt(data, key) 12 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/control: -------------------------------------------------------------------------------- 1 | Provides: postgresql-pgcrypto-openpgp 2 | Section: database 3 | Priority: optional 4 | Maintainer: Per Lejontand 5 | Homepage: https://github.com/johto/postgres/tree/pgcrypto_signatures 6 | Description: Enhanced pgcrypto from the contrib part of PostgreSQL 7 | Extended from https://github.com/johto/postgres/tree/pgcrypto_signatures to 8 | include signature verification and signing 9 | -------------------------------------------------------------------------------- /TABLES/keys.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Keys ( 2 | MainKeyID text not null, 3 | SubKeyID text not null, 4 | PublicKeyring bytea not null, 5 | SecretKeyring bytea, 6 | BankID text not null, 7 | Datestamp timestamptz not null default now(), 8 | PrimaryKey boolean not null default TRUE, 9 | PRIMARY KEY (MainKeyID), 10 | UNIQUE(SubKeyID), 11 | FOREIGN KEY (BankID) REFERENCES Banks(BankID) 12 | ); 13 | 14 | CREATE UNIQUE INDEX ON Keys(BankID) WHERE PrimaryKey IS TRUE; 15 | -------------------------------------------------------------------------------- /FUNCTIONS/update_message_state.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Update_Message_State(_MessageID text, _FromState messagestate, _ToState messagestate) RETURNS BOOLEAN 2 | SET search_path TO public, pg_temp 3 | AS $BODY$ 4 | DECLARE 5 | _OK boolean; 6 | BEGIN 7 | UPDATE Messages SET MessageState = _ToState WHERE MessageID = _MessageID AND MessageState = _FromState RETURNING TRUE INTO STRICT _OK; 8 | RETURN TRUE; 9 | END; 10 | $BODY$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; 11 | -------------------------------------------------------------------------------- /FUNCTIONS/encrypt_sign.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Encrypt_Sign(OUT Cipherdata bytea, _Plaintext text, _EncryptionKeyID text, _SignatureKeyID text) RETURNS bytea AS $BODY$ 2 | SELECT pgp_pub_encrypt_sign_bytea( 3 | convert_to($1,'UTF8'), 4 | EncryptionKey.PublicKeyring, 5 | SignatureKey.SecretKeyring 6 | ) 7 | FROM Keys AS EncryptionKey, 8 | Keys AS SignatureKey 9 | WHERE EncryptionKey.SubKeyID = $2 10 | AND SignatureKey.MainKeyID = $3 11 | $BODY$ LANGUAGE sql VOLATILE; 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAKE_BANKAPI=$(MAKE) -f debian/bankapi/rules 2 | MAKE_POSTGRES=$(MAKE) -f debian/postgresql-pgcrypto-openpgp/rules 3 | 4 | all: deb 5 | 6 | .PHONY: clean 7 | clean: 8 | $(MAKE_BANKAPI) clean 9 | $(MAKE_POSTGRES) clean 10 | 11 | .PHONY: deb 12 | deb: bankapi pgcrypto 13 | 14 | 15 | .PHONY: pgcrypto 16 | pgcrypto: 17 | $(MAKE_POSTGRES) build binary 18 | $(MAKE_POSTGRES) clean 19 | 20 | 21 | .PHONY: bankapi 22 | bankapi: 23 | $(MAKE_BANKAPI) build binary 24 | $(MAKE_BANKAPI) clean 25 | -------------------------------------------------------------------------------- /FUNCTIONS/read_message.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Read_Message(OUT Plaintext text, OUT FromBankID text, OUT ToBankID text, _MessageID text) RETURNS RECORD AS $BODY$ 2 | DECLARE 3 | BEGIN 4 | EXECUTE ' 5 | SELECT 6 | Files.Plaintext, 7 | Messages.FromBankID, 8 | Messages.ToBankID 9 | FROM Messages 10 | INNER JOIN Files ON (Files.FileID = Messages.FileID) 11 | WHERE Messages.MessageID LIKE $1' INTO STRICT Plaintext, FromBankID, ToBankID USING (_MessageID || '%'); 12 | RETURN; 13 | END; 14 | $BODY$ LANGUAGE plpgsql VOLATILE; 15 | -------------------------------------------------------------------------------- /TABLES/messages.sql: -------------------------------------------------------------------------------- 1 | CREATE TYPE messagestate AS ENUM ('SENDING','SENT','UNPROCESSED', 'PROCESSING', 'PROCESSED', 'ERROR'); 2 | 3 | CREATE TABLE Messages ( 4 | MessageID text not null, 5 | MessageType text not null, 6 | FileID text not null, 7 | FromBankID text not null, 8 | ToBankID text not null, 9 | Cipherdata bytea not null, 10 | DeliveryReceipt bytea, 11 | Datestamp timestamptz not null default now(), 12 | Delivered timestamptz, 13 | MessageState messagestate not null, 14 | PRIMARY KEY (MessageID), 15 | FOREIGN KEY (FileID) REFERENCES Files(FileID) 16 | ); 17 | -------------------------------------------------------------------------------- /FUNCTIONS/decrypt_message.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Decrypt_Message(OUT Plaintext text, OUT FromBankID text, OUT ToBankID text, OUT CreationTime timestamptz, _MessageID text) RETURNS RECORD AS $BODY$ 2 | SELECT 3 | Decrypt_Verify.Plaintext, 4 | FromBankKey.BankID, 5 | ToBankKey.BankID, 6 | Decrypt_Verify.CreationTime 7 | FROM Decrypt_Verify(dearmor(Get_Message($1))), Keys AS FromBankKey, Keys AS ToBankKey 8 | WHERE FromBankKey.MainKeyID = Decrypt_Verify.SignatureKeyID 9 | AND ToBankKey.SubKeyID = Decrypt_Verify.EncryptionKeyID 10 | $BODY$ LANGUAGE sql VOLATILE; 11 | -------------------------------------------------------------------------------- /FUNCTIONS/register_secret_keyring.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Register_Secret_Keyring(_BankID text, _SecretKeyring text) RETURNS BOOLEAN AS $BODY$ 2 | DECLARE 3 | _MainKeyID text; 4 | _Dearmored bytea; 5 | _SubKeyID text; 6 | _OK boolean; 7 | BEGIN 8 | 9 | _Dearmored := dearmor(_SecretKeyring); 10 | _MainKeyID := pgp_main_key_id(_Dearmored); 11 | _SubKeyID := pgp_key_id(_Dearmored); 12 | 13 | UPDATE Keys SET SecretKeyring = _Dearmored 14 | WHERE MainKeyID = _MainKeyID AND SubKeyID = _SubKeyID AND BankID = _BankID 15 | RETURNING TRUE INTO STRICT _OK; 16 | 17 | RETURN TRUE; 18 | END; 19 | $BODY$ LANGUAGE plpgsql VOLATILE; 20 | -------------------------------------------------------------------------------- /install.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION pgcrypto_openpgp; 2 | 3 | \i TABLES/banks.sql 4 | \i TABLES/keys.sql 5 | \i TABLES/files.sql 6 | \i TABLES/messages.sql 7 | 8 | \i FUNCTIONS/encrypt_sign.sql 9 | \i FUNCTIONS/decrypt_verify.sql 10 | \i FUNCTIONS/create_message.sql 11 | \i FUNCTIONS/get_message.sql 12 | \i FUNCTIONS/get_bank.sql 13 | \i FUNCTIONS/receive_message.sql 14 | \i FUNCTIONS/read_message.sql 15 | \i FUNCTIONS/decrypt_message.sql 16 | \i FUNCTIONS/decode_delivery_receipt.sql 17 | \i FUNCTIONS/list_messages.sql 18 | \i FUNCTIONS/register_bank.sql 19 | \i FUNCTIONS/register_public_keyring.sql 20 | \i FUNCTIONS/register_secret_keyring.sql 21 | \i FUNCTIONS/get_next_unprocessed_message.sql 22 | \i FUNCTIONS/update_message_state.sql 23 | -------------------------------------------------------------------------------- /FUNCTIONS/decrypt_verify.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Decrypt_Verify(OUT EncryptionKeyID text, OUT SignatureKeyID text, OUT Plaintext text, OUT CreationTime timestamptz, _Cipherdata bytea) RETURNS RECORD AS $BODY$ 2 | SELECT 3 | EncryptionKey.SubKeyID, 4 | SignatureKey.MainKeyID, 5 | convert_from( 6 | pgp_pub_decrypt_verify_bytea( 7 | $1, 8 | EncryptionKey.SecretKeyring, 9 | SignatureKey.PublicKeyring 10 | ), 11 | 'UTF8' 12 | ), 13 | (SELECT Creation_Time FROM pgp_pub_signatures($1, EncryptionKey.SecretKeyring, '', TRUE)) 14 | FROM Keys AS EncryptionKey, 15 | Keys AS SignatureKey 16 | WHERE EncryptionKey.SubKeyID = pgp_key_id($1) 17 | AND SignatureKey.MainKeyID = (SELECT KeyID FROM pgp_pub_signatures($1, EncryptionKey.SecretKeyring)) 18 | $BODY$ LANGUAGE sql VOLATILE; 19 | -------------------------------------------------------------------------------- /debian/bankapi/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | case $(/usr/sbin/apache2 -v | head -n1 | cut -f3 -d' ') in 5 | Apache/2.2.*) 6 | APACHESITE=010-bankapi 7 | ;; 8 | Apache/2.4.*) 9 | APACHESITE=010-bankapi.conf 10 | ;; 11 | *) 12 | APACHESITE=010-bankapi.conf 13 | ;; 14 | esac 15 | 16 | case "$1" in 17 | reconfigure) 18 | a2dissite "010-bankapi" 19 | ;;& 20 | configure|reconfigure) 21 | if [ ! -e "/etc/apache2/sites-enabled/$APACHESITE" ]; then 22 | a2ensite -q "010-bankapi" 23 | a2enmod -q cgi 24 | service apache2 restart 25 | fi 26 | ;; 27 | abort-upgrade|abort-remove|abort-deconfigure) 28 | # We don't have to do anything because we didn't do anything in prerm 29 | exit 0 30 | ;; 31 | *) 32 | echo "postinst called with unknown argument \`$1'" >&2 33 | exit 1 34 | ;; 35 | esac 36 | -------------------------------------------------------------------------------- /FUNCTIONS/decode_delivery_receipt.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Decode_Delivery_Receipt(OUT FileID text, OUT FromBankID text, OUT ToBankID text, _DeliveryReceipt text) RETURNS RECORD 2 | SET search_path TO public, pg_temp 3 | AS $BODY$ 4 | UPDATE Messages SET DeliveryReceipt = COALESCE(DeliveryReceipt,dearmor($1)), Delivered = COALESCE(Delivered,now()), MessageState = 'SENT' 5 | FROM Decrypt_Verify(dearmor($1)), 6 | Keys AS FromBankKey, 7 | Keys AS ToBankKey 8 | WHERE FromBankKey.SubKeyID = Decrypt_Verify.EncryptionKeyID 9 | AND ToBankKey.MainKeyID = Decrypt_Verify.SignatureKeyID 10 | AND Messages.FromBankID = FromBankKey.BankID 11 | AND Messages.ToBankID = ToBankKey.BankID 12 | AND Messages.FileID = Decrypt_Verify.Plaintext 13 | RETURNING 14 | Decrypt_Verify.Plaintext, 15 | FromBankKey.BankID, 16 | ToBankKey.BankID 17 | $BODY$ LANGUAGE sql VOLATILE SECURITY DEFINER; 18 | -------------------------------------------------------------------------------- /FUNCTIONS/list_messages.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION List_Messages( 2 | OUT Datestamp timestamptz, 3 | OUT MessageID char(10), 4 | OUT MessageType text, 5 | OUT MessageState messagestate, 6 | OUT FromBankID text, 7 | OUT ToBankID text, 8 | _MessageState messagestate DEFAULT NULL, 9 | _MessageType text DEFAULT NULL, 10 | _FromBankID text DEFAULT NULL, 11 | _ToBankID text DEFAULT NULL 12 | ) RETURNS SETOF RECORD 13 | SET search_path TO public, pg_temp 14 | AS $BODY$ 15 | SELECT 16 | Datestamp::timestamptz(0), 17 | MessageID::char(10), 18 | MessageType, 19 | MessageState, 20 | FromBankID, 21 | ToBankID 22 | FROM Messages 23 | WHERE ($1 IS NULL OR MessageState = $1) 24 | AND ($2 IS NULL OR MessageType = $2) 25 | AND ($3 IS NULL OR FromBankID = $3) 26 | AND ($4 IS NULL OR ToBankID = $4) 27 | ORDER BY Datestamp 28 | $BODY$ LANGUAGE sql VOLATILE SECURITY DEFINER; 29 | -------------------------------------------------------------------------------- /FUNCTIONS/register_public_keyring.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Register_Public_Keyring(_BankID text, _PublicKeyring text) RETURNS BOOLEAN AS $BODY$ 2 | DECLARE 3 | _MainKeyID text; 4 | _Dearmored bytea; 5 | _SubKeyID text; 6 | _OK boolean; 7 | BEGIN 8 | 9 | _Dearmored := dearmor(_PublicKeyring); 10 | _MainKeyID := pgp_main_key_id(_Dearmored); 11 | _SubKeyID := pgp_key_id(_Dearmored); 12 | 13 | IF EXISTS (SELECT 1 FROM Keys WHERE MainKeyID = _MainKeyID AND SubKeyID = _SubKeyID AND BankID = _BankID) THEN 14 | UPDATE Keys SET PublicKeyring = _Dearmored 15 | WHERE MainKeyID = _MainKeyID AND SubKeyID = _SubKeyID AND BankID = _BankID 16 | RETURNING TRUE INTO STRICT _OK; 17 | RETURN TRUE; 18 | END IF; 19 | 20 | INSERT INTO Keys (MainKeyID, SubKeyID, PublicKeyring, BankID) 21 | VALUES (_MainKeyID, _SubKeyID, _Dearmored, _BankID) 22 | RETURNING TRUE INTO STRICT _OK; 23 | 24 | RETURN TRUE; 25 | END; 26 | $BODY$ LANGUAGE plpgsql VOLATILE; 27 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/11910.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 11910 14 | 15 | plpgsql_validator 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224943.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3224943 14 | 15 | processcallgraphbuffers 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225046.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | encrypt_sign 11 | 12 | 13 | 3225005 14 | 15 | 16 | keys 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225047.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | decrypt_verify 11 | 12 | 13 | 3225005 14 | 15 | 16 | keys 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225049.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | get_message 11 | 12 | 13 | 3225032 14 | 15 | 16 | messages 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/patches/9.1.13/101-pxmemset.patch: -------------------------------------------------------------------------------- 1 | --- a/pgp-pgsql.c 2014-03-17 20:37:27.000000000 +0100 2 | +++ b/pgp-pgsql.c 2014-08-12 09:04:17.227360990 +0200 3 | @@ -31,8 +31,8 @@ 4 | 5 | #include "postgres.h" 6 | 7 | -#include "fmgr.h" 8 | -#include "parser/scansup.h" 9 | +#define px_memset(a,b,c) memset(a,b,c) 10 | + 11 | #include "mb/pg_wchar.h" 12 | #include "utils/builtins.h" 13 | 14 | @@ -40,6 +40,8 @@ 15 | #include "px.h" 16 | #include "pgp.h" 17 | 18 | +#include "fmgr.h" 19 | +#include "parser/scansup.h" 20 | /* 21 | * public functions 22 | */ 23 | @@ -89,7 +91,7 @@ 24 | 25 | px_add_entropy(sha1, 20); 26 | 27 | - memset(sha1, 0, 20); 28 | + px_memset(sha1, 0, 20); 29 | } 30 | 31 | /* 32 | @@ -131,7 +133,7 @@ 33 | add_block_entropy(md, data3); 34 | 35 | px_md_free(md); 36 | - memset(rnd, 0, sizeof(rnd)); 37 | + px_memset(rnd, 0, sizeof(rnd)); 38 | } 39 | 40 | /* 41 | @@ -169,7 +171,7 @@ 42 | static void 43 | clear_and_pfree(text *p) 44 | { 45 | - memset(p, 0, VARSIZE(p)); 46 | + px_memset(p, 0, VARSIZE(p)); 47 | pfree(p); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Trustly Group AB 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225051.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | read_message 11 | 12 | 13 | 3225032 14 | 15 | 16 | messages 17 | 18 | 19 | 20 | 21 | 3225005 22 | 23 | 24 | keys 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /FUNCTIONS/create_message.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Create_Message(OUT MessageID text, _Plaintext text, _MessageType text, _FromBankID text, _ToBankID text) RETURNS TEXT 2 | SET search_path TO public, pg_temp 3 | AS $BODY$ 4 | DECLARE 5 | _FileID text; 6 | _SignatureKeyID text; 7 | _EncryptionKeyID text; 8 | _Cipherdata bytea; 9 | _OK boolean; 10 | BEGIN 11 | 12 | _FileID := encode(digest(_Plaintext,'sha512'),'hex'); 13 | IF NOT EXISTS (SELECT 1 FROM Files WHERE FileID = _FileID) THEN 14 | INSERT INTO Files (FileID, Plaintext) VALUES (_FileID, _Plaintext) RETURNING TRUE INTO STRICT _OK; 15 | END IF; 16 | 17 | SELECT Messages.MessageID INTO MessageID FROM Messages WHERE FileID = _FileID AND MessageType = _MessageType AND FromBankID = _FromBankID AND ToBankID = _ToBankID; 18 | IF FOUND THEN 19 | RETURN; 20 | END IF; 21 | 22 | SELECT MainKeyID INTO STRICT _SignatureKeyID FROM Keys WHERE BankID = _FromBankID AND PrimaryKey IS TRUE; 23 | SELECT SubKeyID INTO STRICT _EncryptionKeyID FROM Keys WHERE BankID = _ToBankID AND PrimaryKey IS TRUE; 24 | 25 | _Cipherdata := Encrypt_Sign(_Plaintext, _EncryptionKeyID, _SignatureKeyID); 26 | MessageID := encode(digest(_Cipherdata,'sha512'),'hex'); 27 | 28 | INSERT INTO Messages (MessageID, MessageType, FileID, FromBankID, ToBankID, Cipherdata, MessageState) 29 | VALUES (MessageID, _MessageType, _FileID, _FromBankID, _ToBankID, _Cipherdata, 'SENDING') 30 | RETURNING TRUE INTO STRICT _OK; 31 | 32 | RETURN; 33 | END; 34 | $BODY$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; 35 | -------------------------------------------------------------------------------- /doc/key_management.md: -------------------------------------------------------------------------------- 1 | ## Web of trust 2 | >In cryptography, a web of trust is a concept used in PGP, GnuPG, and other OpenPGP-compatible systems to establish the authenticity of the binding between a public key and its owner. Its decentralized trust model is an alternative to the centralized trust model of a public key infrastructure (PKI), which relies exclusively on a certificate authority (or a hierarchy of such). As with computer networks, there are many independent webs of trust, and any user (through their identity certificate) can be a part of, and a link between, multiple webs. 3 | (http://en.wikipedia.org/wiki/Web_of_trust) 4 | 5 | The more banks that exchange public keys, the stronger will a potential "web of trust" be where each bank would sign the other banks public keys, 6 | allowing others to trust an unknown bank's public key because it has been signed by multiple other banks, which public keys can be found on their websites. 7 | 8 | But before you have a lot of banks who have migrated to this new way of communicating, SWIFT is an excellent way of doing the exchange of public keys. 9 | It's excellent because all banks already know how to send each other manual text messages via SWIFT, so they can just copy/paste the ASCII armored public keyring in the SWIFT terminal and send it, probably in a FIN MT999, which is a SWIFT message type which allows free text messages. 10 | 11 | The exchange of public keys via SWIFT could also be automated, but we can worry about that when the number of BankAPI banks reach a sufficiently high number to motivate automating the task. 12 | -------------------------------------------------------------------------------- /FUNCTIONS/get_next_unprocessed_message.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Get_Next_Unprocessed_Message( 2 | OUT Datestamp timestamptz, 3 | OUT MessageID char(10), 4 | OUT Plaintext text, 5 | INOUT MessageType text DEFAULT NULL, 6 | INOUT FromBankID text DEFAULT NULL, 7 | INOUT ToBankID text DEFAULT NULL 8 | ) RETURNS RECORD 9 | SET search_path TO public, pg_temp 10 | AS $BODY$ 11 | DECLARE 12 | _OK boolean; 13 | BEGIN 14 | SELECT 15 | Messages.Datestamp, 16 | Messages.MessageID, 17 | Messages.MessageType, 18 | Messages.FromBankID, 19 | Messages.ToBankID, 20 | Files.Plaintext 21 | INTO 22 | Datestamp, 23 | MessageID, 24 | MessageType, 25 | FromBankID, 26 | ToBankID, 27 | Plaintext 28 | FROM Messages 29 | INNER JOIN Files ON (Files.FileID = Messages.FileID) 30 | WHERE Messages.MessageState = 'UNPROCESSED' 31 | AND (Get_Next_Unprocessed_Message.MessageType IS NULL OR Messages.MessageType = Get_Next_Unprocessed_Message.MessageType) 32 | AND (Get_Next_Unprocessed_Message.FromBankID IS NULL OR Messages.FromBankID = Get_Next_Unprocessed_Message.FromBankID) 33 | AND (Get_Next_Unprocessed_Message.ToBankID IS NULL OR Messages.ToBankID = Get_Next_Unprocessed_Message.ToBankID) 34 | ORDER BY Messages.Datestamp 35 | LIMIT 1 36 | FOR UPDATE OF Messages; 37 | IF NOT FOUND THEN 38 | RETURN; 39 | END IF; 40 | UPDATE Messages SET MessageState = 'PROCESSING' 41 | WHERE Messages.MessageID = Get_Next_Unprocessed_Message.MessageID 42 | AND Messages.MessageState = 'UNPROCESSED' 43 | RETURNING TRUE INTO STRICT _OK; 44 | RETURN; 45 | END; 46 | $BODY$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; 47 | -------------------------------------------------------------------------------- /doc/call_graph/t3225049.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225049e3224994 14 | 15 | 16 | armor 17 | 18 | 19 | 20 | 21 | t3225049e3225049 22 | 23 | 24 | get_message 25 | 26 | 27 | 28 | 29 | t3225049e3225049->t3225049e3224994 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /debian/postgresql-pgcrypto-openpgp/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | VERSION=1.3-1 4 | PGMINORVERSION=$$(pg_config --version | sed 's/.* //') 5 | PGVERSION=$$(echo "$(PGMINORVERSION)" | cut -f1-2 -d.) 6 | PGMAJORVERSION=$$(echo "$(PGVERSION)" | cut -f1 -d.) 7 | PACKAGE=postgresql-pgcrypto-openpgp-$(PGVERSION) 8 | ROOT=$(PWD) 9 | DEBIAN=$(ROOT)/debian/postgresql-pgcrypto-openpgp 10 | PACKAGEDIR=$(ROOT)/$(PACKAGE)-$(VERSION) 11 | BUILDDIR=$(ROOT)/$(PACKAGE)-$(VERSION)-build 12 | CP=cp -p 13 | 14 | build: clean 15 | if [ ! -f "$(DEBIAN)/control-$(PGVERSION)" ]; then \ 16 | echo "No control for PostgreSQL $(PGVERSION)"; \ 17 | exit 1; \ 18 | fi 19 | mkdir -p "$(PACKAGEDIR)" \ 20 | "$(PACKAGEDIR)/DEBIAN" 21 | mkdir -p "$(BUILDDIR)" 22 | (cd "$(BUILDDIR)"; apt-get source postgresql-contrib-$(PGVERSION)) 23 | (cd "$(BUILDDIR)" ; \ 24 | set -e; \ 25 | cd */contrib/pgcrypto; \ 26 | for file in $$(find $(DEBIAN)/patches/$(PGMINORVERSION) $(DEBIAN)/patches/$(PGVERSION) $(DEBIAN)/patches/$(PGMAJORVERSION) -name \*.patch | cut -c$$(echo "$(DEBIAN)" | wc -c)- | sort -t/ -k 4n); do \ 27 | echo "PATCH $(DEBIAN)$$file"; \ 28 | patch -t -p1 < "$(DEBIAN)$$file"; \ 29 | done; \ 30 | echo "Done patching"; \ 31 | $(MAKE); \ 32 | $(MAKE) DESTDIR=$(PACKAGEDIR) install; \ 33 | ) 34 | 35 | install: 36 | 37 | binary: build 38 | echo "Package: $(PACKAGE)" > "$(PACKAGEDIR)/DEBIAN/control" 39 | echo "Version: $(VERSION)" >> "$(PACKAGEDIR)/DEBIAN/control" 40 | echo "Architecture: $$(dpkg-architecture -qDEB_BUILD_ARCH)" >> "$(PACKAGEDIR)/DEBIAN/control" 41 | cat "$(DEBIAN)/control-$(PGVERSION)" >> "$(PACKAGEDIR)/DEBIAN/control" 42 | cat "$(DEBIAN)/control" >> "$(PACKAGEDIR)/DEBIAN/control" 43 | dpkg-deb -b "$(PACKAGEDIR)" "$(ROOT)" 44 | 45 | clean: 46 | rm -rf "$(PACKAGEDIR)" 47 | rm -rf "$(BUILDDIR)" 48 | 49 | .PHONY: build binary binary-arch binary-indep clean install configure 50 | -------------------------------------------------------------------------------- /doc/call_graph/t3225046.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225046e3224977 14 | 15 | 16 | pgp_pub_encrypt_sign_bytea 17 | 18 | 19 | 20 | 21 | t3225046e3225046 22 | 23 | 24 | encrypt_sign 25 | 26 | 27 | 28 | 29 | t3225046e3225046->t3225046e3224977 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo-bankapi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # This script is based upon the databases configurd in demo.sh script. 5 | # 6 | # Deliver a message from TESTBANK001 to TESTBANK002 using sendbankmessage. 7 | # In order for this to work it requires that there is a web server listening 8 | # accepting incoming connections on http://localhost/bankapi that is connected 9 | # to the database for TESTBANK002 here. It also requires that TESTBANK001 10 | # database is initialized as in demo.sh 11 | # 12 | # Install the debian packages for bankapi to get the cgi listening, then modify 13 | # /var/www/bankapi/bankapi.py to point to the TESTBANK002 database (dbname=TESTBANK002) 14 | echo "Sending message using bankapi" 15 | echo 'This is another secret message from TESTBANK001 to TESTBANK002' | \ 16 | /usr/bin/bankapi send -d TESTBANK001 TESTBANK001 TESTBANK002 text/plain - 17 | 18 | 19 | echo "Searching and adding the bankapi key for Trustly into TESTBANK001 database." 20 | echo "Please respond to the questions interactivly" 21 | echo "Import key with id: 2E4C6F05FC0156943ABA5EBA41F51DC2C04EA656" 22 | /usr/bin/bankapi add -d TESTBANK001 TRLYSESSXXX 23 | 24 | # List the messages present in both databases. The information in the two 25 | # databases should really be the same as we update and keep all the message 26 | # information in sync between the two. 27 | echo "List messages in the system of TESTBANK001" 28 | /usr/bin/bankapi list -d TESTBANK001 29 | 30 | echo "List messages in the system of TESTBANK002" 31 | /usr/bin/bankapi list -d TESTBANK002 32 | 33 | echo "The two lists of messages should be identical (apart from minor differences in timestamps)" 34 | 35 | # Now fetch the last message in the list from TESTBANK002 database and display it using read 36 | messagerow=$(/usr/bin/bankapi list -d TESTBANK002 | tail -n2 | head -n1) 37 | echo "Displaying the last message in TESTBANK002: $messagerow" 38 | messageid=$(echo "$messagerow" | cut -c21-30) 39 | /usr/bin/bankapi read "$messageid" 40 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225048.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | create_message 11 | 12 | 13 | 3225023 14 | 15 | 16 | files 17 | 18 | 19 | 20 | 21 | 3225032 22 | 23 | 24 | messages 25 | 26 | 27 | 28 | 29 | 3225032->3225023 30 | 31 | 32 | 33 | 34 | 3225005 35 | 36 | 37 | keys 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225050.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | receive_message 11 | 12 | 13 | 3225023 14 | 15 | 16 | files 17 | 18 | 19 | 20 | 21 | 3225032 22 | 23 | 24 | messages 25 | 26 | 27 | 28 | 29 | 3225032->3225023 30 | 31 | 32 | 33 | 34 | 3225005 35 | 36 | 37 | keys 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3224943.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | processcallgraphbuffers 11 | 12 | 13 | 3224895 14 | 15 | 16 | callgraphs 17 | 18 | 19 | 20 | 21 | 3224921 22 | 23 | 24 | tableusage 25 | 26 | 27 | 28 | 29 | 3224908 30 | 31 | 32 | edges 33 | 34 | 35 | 36 | 37 | 3224908->3224895 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/tlf3225052.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | decode_delivery_receipt 11 | 12 | 13 | 3225023 14 | 15 | 16 | files 17 | 18 | 19 | 20 | 21 | 3225032 22 | 23 | 24 | messages 25 | 26 | 27 | 28 | 29 | 3225032->3225023 30 | 31 | 32 | 33 | 34 | 3225005 35 | 36 | 37 | keys 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /debian/bankapi/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | VERSION=1.0-1 4 | PACKAGE=bankapi 5 | ROOT=$(PWD) 6 | DEBIAN=$(ROOT)/debian/bankapi 7 | PACKAGEDIR=$(ROOT)/$(PACKAGE)-$(VERSION) 8 | CP=cp -p 9 | APACHEVERSION=$$(/usr/sbin/apache2 -v | head -n1 | cut -f2 -d/ | cut -f1-2 -d.) 10 | 11 | build: clean 12 | if [ ! -f /usr/sbin/apache2 ]; then \ 13 | echo "You need to install apache2 in order to build bankapi"; \ 14 | exit 1; \ 15 | fi 16 | mkdir -p "$(PACKAGEDIR)" \ 17 | "$(PACKAGEDIR)/usr/share/bankapi" \ 18 | "$(PACKAGEDIR)/etc/apache2/sites-available" \ 19 | "$(PACKAGEDIR)/var/www/bankapi" \ 20 | "$(PACKAGEDIR)/usr/bin" \ 21 | "$(PACKAGEDIR)/DEBIAN" 22 | $(CP) "$(ROOT)/install.sql" "$(PACKAGEDIR)/usr/share/bankapi/install.sql" 23 | cat "$(ROOT)/misc/roles.sql" >> "$(PACKAGEDIR)/usr/share/bankapi/install.sql" 24 | cat "$(ROOT)/misc/pg_hba.sql" >> "$(PACKAGEDIR)/usr/share/bankapi/install.sql" 25 | if [ $(APACHEVERSION) = '2.2' ]; then \ 26 | $(CP) "$(ROOT)/etc/apache2/sites-available/010-bankapi.conf" "$(PACKAGEDIR)/etc/apache2/sites-available/010-bankapi"; \ 27 | else \ 28 | $(CP) "$(ROOT)/etc/apache2/sites-available/010-bankapi.conf" "$(PACKAGEDIR)/etc/apache2/sites-available/010-bankapi.conf"; \ 29 | fi 30 | $(CP) "$(ROOT)/cgi/bankapi.py" "$(PACKAGEDIR)/var/www/bankapi/bankapi.py" 31 | $(CP) "$(ROOT)/bin/bankapi" "$(PACKAGEDIR)/usr/bin/bankapi" 32 | $(CP) "$(DEBIAN)/postinst" "$(PACKAGEDIR)/DEBIAN/postinst" 33 | $(CP) "$(DEBIAN)/postrm" "$(PACKAGEDIR)/DEBIAN/postrm" 34 | $(CP) -r "$(ROOT)/TABLES" "$(PACKAGEDIR)/usr/share/bankapi" 35 | $(CP) -r "$(ROOT)/FUNCTIONS" "$(PACKAGEDIR)/usr/share/bankapi" 36 | $(CP) -r "$(ROOT)/testdata" "$(PACKAGEDIR)/usr/share/bankapi" 37 | 38 | install: 39 | 40 | binary: binary-indep binary-arch 41 | echo "Version: $(VERSION)" > "$(PACKAGEDIR)/DEBIAN/control" 42 | cat "$(DEBIAN)/control" >> "$(PACKAGEDIR)/DEBIAN/control" 43 | if [ $(APACHEVERSION) = '2.2' ]; then \ 44 | echo "/etc/apache2/sites-available/010-bankapi" > "$(PACKAGEDIR)/DEBIAN/conffiles"; \ 45 | else \ 46 | echo "/etc/apache2/sites-available/010-bankapi.conf" > "$(PACKAGEDIR)/DEBIAN/conffiles"; \ 47 | fi 48 | dpkg-deb -b "$(PACKAGEDIR)" "$(ROOT)" 49 | 50 | clean: 51 | rm -rf "$(PACKAGEDIR)" 52 | 53 | .PHONY: build binary binary-arch binary-indep clean install configure 54 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225049.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225051 14 | 15 | 16 | read_message 17 | 18 | 19 | 20 | 21 | 3225049 22 | 23 | get_message 24 | 25 | 26 | 3225051->3225049 27 | 28 | 29 | 30 | 31 | 3224994 32 | 33 | 34 | armor 35 | 36 | 37 | 38 | 39 | 3225049->3224994 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224946.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225048 14 | 15 | 16 | create_message 17 | 18 | 19 | 20 | 21 | 3224946 22 | 23 | digest 24 | 25 | 26 | 3225048->3224946 27 | 28 | 29 | 30 | 31 | 3225050 32 | 33 | 34 | receive_message 35 | 36 | 37 | 38 | 39 | 3225050->3224946 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224947.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225048 14 | 15 | 16 | create_message 17 | 18 | 19 | 20 | 21 | 3224947 22 | 23 | digest 24 | 25 | 26 | 3225048->3224947 27 | 28 | 29 | 30 | 31 | 3225050 32 | 33 | 34 | receive_message 35 | 36 | 37 | 38 | 39 | 3225050->3224947 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /FUNCTIONS/receive_message.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION Receive_Message(OUT DeliveryReceipt text, _Ciphertext text) RETURNS TEXT 2 | SET search_path TO public, pg_temp 3 | AS $BODY$ 4 | DECLARE 5 | _Cipherdata bytea; 6 | _MessageID text; 7 | _MessageType text; 8 | _EncryptionKeyID text; 9 | _SignatureKeyID text; 10 | _Plaintext text; 11 | _DeliveryReceipt bytea; 12 | _SecretKey bytea; 13 | _PublicKey bytea; 14 | _MessageData bytea; 15 | _OK boolean; 16 | _FromBankID text; 17 | _ToBankID text; 18 | _Message text; 19 | _FileID text; 20 | _CreationTime timestamptz; 21 | BEGIN 22 | 23 | _Cipherdata := dearmor(_Ciphertext); 24 | _MessageType := pgp_armor_header(_Ciphertext,'Comment'); 25 | IF _MessageType IS NULL THEN 26 | RAISE EXCEPTION 'ERROR_MESSAGE_TYPE_UNDEFINED The PGP armor header "Comment" needs to be set to the message type'; 27 | END IF; 28 | 29 | _MessageID := encode(digest(_Cipherdata, 'sha512'),'hex'); 30 | 31 | SELECT EncryptionKeyID, SignatureKeyID, Plaintext, CreationTime 32 | INTO _EncryptionKeyID, _SignatureKeyID, _Plaintext, _CreationTime 33 | FROM Decrypt_Verify(_Cipherdata); 34 | 35 | SELECT BankID INTO STRICT _FromBankID FROM Keys WHERE MainKeyID = _SignatureKeyID; 36 | SELECT BankID INTO STRICT _ToBankID FROM Keys WHERE SubKeyID = _EncryptionKeyID; 37 | 38 | _FileID := encode(digest(_Plaintext, 'sha512'),'hex'); 39 | 40 | IF NOT EXISTS (SELECT 1 FROM Files WHERE FileID = _FileID) THEN 41 | INSERT INTO Files (FileID, Plaintext) VALUES (_FileID, _Plaintext) RETURNING TRUE INTO STRICT _OK; 42 | END IF; 43 | 44 | SELECT Messages.DeliveryReceipt INTO _DeliveryReceipt FROM Messages WHERE MessageID = _MessageID; 45 | IF NOT FOUND THEN 46 | INSERT INTO Messages ( MessageID, MessageType, FileID, FromBankID, ToBankID, Cipherdata, Datestamp, MessageState) 47 | VALUES (_MessageID, _MessageType, _FileID, _FromBankID, _ToBankID, _Cipherdata, _CreationTime, 'UNPROCESSED') 48 | RETURNING TRUE INTO STRICT _OK; 49 | END IF; 50 | IF _DeliveryReceipt IS NULL THEN 51 | _DeliveryReceipt := Encrypt_Sign( 52 | _Plaintext := _FileID, 53 | _EncryptionKeyID := FromBankKey.SubKeyID, 54 | _SignatureKeyID := ToBankKey.MainKeyID 55 | ) FROM Keys AS FromBankKey, 56 | Keys AS ToBankKey 57 | WHERE FromBankKey.BankID = _FromBankID 58 | AND FromBankKey.PrimaryKey IS TRUE 59 | AND ToBankKey.BankID = _ToBankID 60 | AND ToBankKey.PrimaryKey IS TRUE; 61 | IF _DeliveryReceipt IS NULL THEN 62 | RAISE EXCEPTION 'ERROR_ENCRYPT_SIGN_FAILED FileID % EncryptionKeyID % SignatureKeyID %', _FileID, _SignatureKeyID, _EncryptionKeyID; 63 | END IF; 64 | UPDATE Messages SET DeliveryReceipt = _DeliveryReceipt, Delivered = now() WHERE Messages.MessageID = _MessageID AND Messages.DeliveryReceipt IS NULL AND Messages.Delivered IS NULL RETURNING TRUE INTO STRICT _OK; 65 | END IF; 66 | 67 | DeliveryReceipt := armor(_DeliveryReceipt); 68 | RETURN; 69 | END; 70 | $BODY$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; 71 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/r3224908.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | edges 11 | 12 | 13 | 3224943 14 | 15 | 16 | processcallgraphbuffers 17 | 18 | 19 | 20 | 21 | 3224908 22 | 23 | edges 24 | 25 | sequential scans: 0 26 | 27 | avg seq tuples: 0 28 | 29 | index scans: 1 30 | 31 | avg idx tuples: 1.00 32 | 33 | inserted rows: 3 34 | 35 | updated rows: 1 36 | 37 | deleted rows: 0 38 | 39 | 40 | 3224943->3224908:name 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/r3224895.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | callgraphs 11 | 12 | 13 | 3224943 14 | 15 | 16 | processcallgraphbuffers 17 | 18 | 19 | 20 | 21 | 3224895 22 | 23 | callgraphs 24 | 25 | sequential scans: 0 26 | 27 | avg seq tuples: 0 28 | 29 | index scans: 7 30 | 31 | avg idx tuples: 0.57 32 | 33 | inserted rows: 3 34 | 35 | updated rows: 1 36 | 37 | deleted rows: 0 38 | 39 | 40 | 3224943->3224895:name 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/r3224921.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | tableusage 11 | 12 | 13 | 3224943 14 | 15 | 16 | processcallgraphbuffers 17 | 18 | 19 | 20 | 21 | 3224921 22 | 23 | tableusage 24 | 25 | sequential scans: 0 26 | 27 | avg seq tuples: 0 28 | 29 | index scans: 4 30 | 31 | avg idx tuples: 1.00 32 | 33 | inserted rows: 3 34 | 35 | updated rows: 2 36 | 37 | deleted rows: 0 38 | 39 | 40 | 3224943->3224921:name 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /cgi/bankapi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import json 6 | import psycopg2 7 | import psycopg2.extras 8 | import re 9 | import datetime 10 | import os 11 | 12 | pg_connect_str = "dbname=bankapi" 13 | pg_conn = None 14 | 15 | debug_incoming_data = False 16 | debug_path = '/tmp/bankapi' 17 | 18 | def respond(result=None, error=None, httperrorcode=None): 19 | global pg_conn 20 | 21 | if result is not None: 22 | data = """Status: 200 OK 23 | Content-Type: text/plain 24 | 25 | """ + result 26 | else: 27 | if httperrorcode is None: 28 | httperrorcode = 500 29 | if error is None: 30 | error = 'Unknown error' 31 | 32 | data = """Status: %s %s 33 | """ % (httperrorcode, error) 34 | 35 | print data 36 | debug_request('out', data) 37 | 38 | if pg_conn is not None: 39 | pg_conn.close() 40 | 41 | sys.exit(0) 42 | 43 | def error_log(s): 44 | sys.stderr.write("%s\n" % (s)) 45 | 46 | def debug_request(extension, data): 47 | global debug_incoming_data 48 | global debug_path 49 | 50 | if debug_incoming_data == True: 51 | td = datetime.datetime.today() 52 | path = debug_path + '/' + td.strftime('%Y%m%d') 53 | filename = path + '/' + td.isoformat() 54 | 55 | if not os.path.exists(path): 56 | os.makedirs(path) 57 | 58 | fh = open(filename + '.' + extension, 'w') 59 | fh.write(data) 60 | 61 | try: 62 | pg_conn = psycopg2.connect(pg_connect_str) 63 | except psycopg2.Error as e: 64 | error_log("Failed to connect to database: error=%s, code=%s" % (e.pgerror, e.pgcode)) 65 | respond(error='Failed to connect to database', httperrorcode=500) 66 | 67 | try: 68 | indata = sys.stdin.read().strip() 69 | except Exception as e: 70 | respond(error='Failed to decode input data', httperrorcode=400) 71 | 72 | if indata is None or len(indata) == 0: 73 | respond(error='No data', httperrorcode=400) 74 | 75 | debug_request('in', indata) 76 | 77 | cur = pg_conn.cursor(cursor_factory=psycopg2.extras.DictCursor) 78 | 79 | sqldata = None 80 | try: 81 | cur.execute('SELECT DeliveryReceipt FROM receive_message(%s)', 82 | [indata]) 83 | 84 | sqldata = cur.fetchone() 85 | pg_conn.commit() 86 | respond(result=sqldata['deliveryreceipt']) 87 | except psycopg2.InternalError as e: 88 | pg_conn.rollback() 89 | error_log("Call to Receive_Message failed: error=%s, code=%s" % (e.pgerror, e.pgcode)) 90 | 91 | mg = re.match('ERROR:\s+(ERROR_\S+)', e.pgerror) 92 | if mg is not None: 93 | respond(error=mg.group(1), httperrorcode=500) 94 | else: 95 | respond(error='Receieve processing failed', httperrorcode=500) 96 | 97 | except psycopg2.ProgrammingError as e: 98 | error_log("Call to Receive_Message failed with ProgrammingError: error=%s, code=%s" % (e.pgerror, e.pgcode)) 99 | pg_conn.rollback() 100 | respond(error='Recieve processing failed', httperrorcode=500) 101 | 102 | sys.exit(0) 103 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224994.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225050 14 | 15 | 16 | receive_message 17 | 18 | 19 | 20 | 21 | 3224994 22 | 23 | armor 24 | 25 | 26 | 3225050->3224994 27 | 28 | 29 | 30 | 31 | 3225049 32 | 33 | 34 | get_message 35 | 36 | 37 | 38 | 39 | 3225049->3224994 40 | 41 | 42 | 43 | 44 | 3225051 45 | 46 | 47 | read_message 48 | 49 | 50 | 51 | 52 | 3225051->3225049 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224995.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225052 14 | 15 | 16 | decode_delivery_receipt 17 | 18 | 19 | 20 | 21 | 3224995 22 | 23 | dearmor 24 | 25 | 26 | 3225052->3224995 27 | 28 | 29 | 30 | 31 | 3225050 32 | 33 | 34 | receive_message 35 | 36 | 37 | 38 | 39 | 3225050->3224995 40 | 41 | 42 | 43 | 44 | 3225051 45 | 46 | 47 | read_message 48 | 49 | 50 | 51 | 52 | 3225051->3224995 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225046.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225048 14 | 15 | 16 | create_message 17 | 18 | 19 | 20 | 21 | 3225046 22 | 23 | encrypt_sign 24 | 25 | 26 | 3225048->3225046 27 | 28 | 29 | 30 | 31 | 3224977 32 | 33 | 34 | pgp_pub_encrypt_sign_bytea 35 | 36 | 37 | 38 | 39 | 3225046->3224977 40 | 41 | 42 | 43 | 44 | 3225050 45 | 46 | 47 | receive_message 48 | 49 | 50 | 51 | 52 | 3225050->3225046 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224977.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225046 14 | 15 | 16 | encrypt_sign 17 | 18 | 19 | 20 | 21 | 3224977 22 | 23 | pgp_pub_encrypt_sign_bytea 24 | 25 | 26 | 3225046->3224977 27 | 28 | 29 | 30 | 31 | 3225048 32 | 33 | 34 | create_message 35 | 36 | 37 | 38 | 39 | 3225048->3225046 40 | 41 | 42 | 43 | 44 | 3225050 45 | 46 | 47 | receive_message 48 | 49 | 50 | 51 | 52 | 3225050->3225046 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/call_graph/t3225047.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225047e3224986 14 | 15 | 16 | pgp_pub_decrypt_verify_bytea 17 | 18 | 19 | 20 | 21 | t3225047e3224989 22 | 23 | 24 | pgp_key_id 25 | 26 | 27 | 28 | 29 | t3225047e3224992 30 | 31 | 32 | pgp_pub_signature_keys 33 | 34 | 35 | 36 | 37 | t3225047e3225047 38 | 39 | 40 | decrypt_verify 41 | 42 | 43 | 44 | 45 | t3225047e3225047->t3225047e3224986 46 | 47 | 48 | 49 | 50 | t3225047e3225047->t3225047e3224989 51 | 52 | 53 | 54 | 55 | t3225047e3225047->t3225047e3224992 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225048.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225048 14 | 15 | create_message 16 | 17 | 18 | 3225046 19 | 20 | 21 | encrypt_sign 22 | 23 | 24 | 25 | 26 | 3225048->3225046 27 | 28 | 29 | 30 | 31 | 3224946 32 | 33 | 34 | digest 35 | 36 | 37 | 38 | 39 | 3225048->3224946 40 | 41 | 42 | 43 | 44 | 3224947 45 | 46 | 47 | digest 48 | 49 | 50 | 51 | 52 | 3225048->3224947 53 | 54 | 55 | 56 | 57 | 3224977 58 | 59 | 60 | pgp_pub_encrypt_sign_bytea 61 | 62 | 63 | 64 | 65 | 3225046->3224977 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /testdata/test-secretkeyring-TESTBANK001.sql: -------------------------------------------------------------------------------- 1 | SELECT Register_Secret_Keyring( 2 | _BankID := 'TESTBANK001', 3 | _SecretKeyring := $$ 4 | -----BEGIN PGP PRIVATE KEY BLOCK----- 5 | Version: GnuPG/MacGPG2 v2.0.22 (Darwin) 6 | Comment: GPGTools - http://gpgtools.org 7 | 8 | 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 59 | w56kxSCWNuEoDoRUv26Z 60 | =cgV5 61 | -----END PGP PRIVATE KEY BLOCK----- 62 | $$); 63 | -------------------------------------------------------------------------------- /testdata/test-secretkeyring-TESTBANK002.sql: -------------------------------------------------------------------------------- 1 | SELECT Register_Secret_Keyring( 2 | _BankID := 'TESTBANK002', 3 | _SecretKeyring := $$ 4 | -----BEGIN PGP PRIVATE KEY BLOCK----- 5 | Version: GnuPG/MacGPG2 v2.0.22 (Darwin) 6 | Comment: GPGTools - http://gpgtools.org 7 | 8 | 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 59 | XVVvRmZKrf6b7dGhhdAJ 60 | =7TYY 61 | -----END PGP PRIVATE KEY BLOCK----- 62 | $$); 63 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224989.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225047 14 | 15 | 16 | decrypt_verify 17 | 18 | 19 | 20 | 21 | 3224989 22 | 23 | pgp_key_id 24 | 25 | 26 | 3225047->3224989 27 | 28 | 29 | 30 | 31 | 3225052 32 | 33 | 34 | decode_delivery_receipt 35 | 36 | 37 | 38 | 39 | 3225052->3225047 40 | 41 | 42 | 43 | 44 | 3225050 45 | 46 | 47 | receive_message 48 | 49 | 50 | 51 | 52 | 3225050->3225047 53 | 54 | 55 | 56 | 57 | 3225051 58 | 59 | 60 | read_message 61 | 62 | 63 | 64 | 65 | 3225051->3225047 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224992.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225047 14 | 15 | 16 | decrypt_verify 17 | 18 | 19 | 20 | 21 | 3224992 22 | 23 | pgp_pub_signature_keys 24 | 25 | 26 | 3225047->3224992 27 | 28 | 29 | 30 | 31 | 3225052 32 | 33 | 34 | decode_delivery_receipt 35 | 36 | 37 | 38 | 39 | 3225052->3225047 40 | 41 | 42 | 43 | 44 | 3225050 45 | 46 | 47 | receive_message 48 | 49 | 50 | 51 | 52 | 3225050->3225047 53 | 54 | 55 | 56 | 57 | 3225051 58 | 59 | 60 | read_message 61 | 62 | 63 | 64 | 65 | 3225051->3225047 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3224986.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225047 14 | 15 | 16 | decrypt_verify 17 | 18 | 19 | 20 | 21 | 3224986 22 | 23 | pgp_pub_decrypt_verify_bytea 24 | 25 | 26 | 3225047->3224986 27 | 28 | 29 | 30 | 31 | 3225052 32 | 33 | 34 | decode_delivery_receipt 35 | 36 | 37 | 38 | 39 | 3225052->3225047 40 | 41 | 42 | 43 | 44 | 3225050 45 | 46 | 47 | receive_message 48 | 49 | 50 | 51 | 52 | 3225050->3225047 53 | 54 | 55 | 56 | 57 | 3225051 58 | 59 | 60 | read_message 61 | 62 | 63 | 64 | 65 | 3225051->3225047 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /testdata/test-publickeyrings.sql: -------------------------------------------------------------------------------- 1 | SELECT Register_Public_Keyring ( 2 | _BankID := 'TESTBANK001', 3 | _PublicKeyring := $$ 4 | -----BEGIN PGP PUBLIC KEY BLOCK----- 5 | Version: GnuPG/MacGPG2 v2.0.22 (Darwin) 6 | Comment: GPGTools - http://gpgtools.org 7 | 8 | mQENBFPf0OEBCADf/8T34f3tKi+8zMCABEpTHUMgp3jOPv/rm4YJu7typynP+VE0 9 | /ZnQNmvytySTF1ZljW4vNxypf2b7H5IEkUVBVU33dxrsYf2BKFH+VAFDunfAEFyW 10 | N9QiYKWvfwA13A0mHr/ccjg3UwpAFg1IsFwcRiL8AdTF/uYh4suf1AaBjipAWBny 11 | d48pM4xSZkhcPUhPNBP5unnNI2OReB1xKSmvRtTCBixYVsh6KA0+vg9B4feq9fRu 12 | 0xqgYfZTPLpce7fR1pyZNlnmZoqSSebVfXpzQ4ParTZM3Mwa0Nu0Fzt3mRiTEUwE 13 | dkjR3/pwiz3olRzHi51yimJMCGllAClkXriFABEBAAG0C1RFU1RCQU5LMDAxiQE3 14 | BBMBCgAhBQJT39DhAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEE9cDgMB 15 | QiO5X3MH/A+mf558EoCsIxS7aIcOdMS5beBG7bLBDk9Qm+BlcT+ds0nMw8yIl9Rx 16 | 2hIKWiRE2DFqbQr/CULwhFSuMRmiZ07b62N+JHYdggNY/zJg8YbQN6VnyKq8HMPT 17 | clB0sugzwuV6gq0/jr4lcB9Z2PPTtSRWGyWtbQumqA2eU2LyHRELj7ljN5aL53g7 18 | F5Bz2nq025HZz2aT0vNhSLwIb+2Y6I6VevRVgdY2XxM6zvi+Vbu7JryaHxeVd6XB 19 | qEgEih6qYxoKXcrv8VyJUVDdHCGIUi0FVd/txTPs4xLg9vVVKIUuullAe6TdtNo5 20 | KkBoNPGEInjsPRaVWfJQ/t63fYUu/S+5AQ0EU9/Q4QEIAM8MBTpX84Bm3FxjsrZk 21 | DCnscgBwirZyM3q9gWSLvMG884/F/nBNy8a9HuiYVjQDcvEneFlLW+Qp/9mZ2kNq 22 | K4EbdRffkw1DHKjAbQoSX8I1foKhMQexSuWGXYD4DKrriJ/ZrTDY1PaWMuAYA0uU 23 | xjLSEj9Z4J+H8t7Uo8NiEGxazukRjVESODs5U8j1662/OndQpJUFkBQjSC5c72o2 24 | o86nS0yrjohJnwUXkY0RNjWoxflBHEY6Eey6G/X3kwgrtL+/lrCTUSjfpXIUFDmH 25 | bNddX4CxOSlWvZ3N3jU3h0eqB9TnirwEIxtsuiYttrMShp8BpCpZD2E3NNO2iMpx 26 | CjkAEQEAAYkBHwQYAQoACQUCU9/Q4QIbDAAKCRBPXA4DAUIjufACCACKLh0qcLuU 27 | lmLjNF4FqnulBzOpmRtdnIeRHtVuJ7bTyqAYqWfmVjgn5H2z7/w0bsz0A8JAmdHA 28 | Xh7ewe1mX0wkWmzfs4BZH3NPVmPPoUSYBC7HmEf1yvbtfKsNstT886g2wpk42aUL 29 | vhQ40Sr5m3XhWCFQpxr8uI/qxR6JXNU3PRrj6XFd72rKgnXY+T033Ray/fN0qvic 30 | gMWMc58fnw4MH29lch6bI5xblRYGqjtcFrzatLZBxQIJlsll6rguI9SqArARn8CR 31 | HkQIQQSU/Y0RNMG6SzmaEfp7NlfrYilR9WRW4ixWI7YD1oMlMSRdkoTkw56kxSCW 32 | NuEoDoRUv26Z 33 | =ZOLk 34 | -----END PGP PUBLIC KEY BLOCK----- 35 | $$); 36 | 37 | SELECT Register_Public_Keyring ( 38 | _BankID := 'TESTBANK002', 39 | _PublicKeyring := $$ 40 | -----BEGIN PGP PUBLIC KEY BLOCK----- 41 | Version: GnuPG/MacGPG2 v2.0.22 (Darwin) 42 | Comment: GPGTools - http://gpgtools.org 43 | 44 | mQENBFPf0PUBCAC253nIED1wrcEAMcG3kOJxMUwMQh3MVBEYySzKVlnCjhTrzyj7 45 | IDjX3FerELq3c8SvKrrmErli6l61yxBHU8ToqkjTHa+O+jVtBQV/t+FQKrO5JIIe 46 | b5JqEt5/rCbz7mUxFPxkTtlbP0bG1Ugea8qrAAnDWe3JUhN+duJubv3gkGESUim6 47 | ADt8snMHN7DBUvKSRpVTEuE5iDwHWyheGGbTUWVf9PGfWmlrlbFl1ENwqL/mht31 48 | UfHGyx6XEJf1Ayg5UcuZ4cXKv6SUk2HuQRdmBU9ECNDAode0VEAJ6yrG4BL9vl9m 49 | C1O5ErfjYpXbcbOTfUSHsmxJDHJRFa6VFfIvABEBAAG0C1RFU1RCQU5LMDAyiQE3 50 | BBMBCgAhBQJT39D1AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEAFpgfch 51 | kKGxJ98H/3JKejPgOJdrGzWQ3o0ExxYuJ3s0oiCGB9WkbhRS6dIvB2N1JOop4B43 52 | 1XgIa/59ehwZwQRs3uNyM7J/D/TuFDtuMc76fLjmUp5et5ZQ1Cc3+EBBInowbCCM 53 | z0NZ9mGnmkbf24gITw9v3KC13Zug0bPiA0JEqyrJIUiNGOOO2FENTh1H3xGnX/rr 54 | bro/Od88v62dodgroOLNEMNvqk62QAZvc9XbQRuNTiWKHMm9hfhvkz/9BEfvi59M 55 | o+zR7aTGggFkLY9oVo6GurbPIndEY20MHlCJG3AhFzrNS8x5+LVFkYeiXeEdfLbX 56 | nrhiARuKRfdXMohOPQmnq0Gv/UoVIQG5AQ0EU9/Q9QEIAMe4adMjPZckSSwZfSNC 57 | AzuXxrFhtypCZkWaJBhb+qaA5jnbg72XrTzdT/6N0vHJpMWA56SL+GlLuYNEuSlA 58 | TD4qaqZ4SOWWcVYc8Jhb6F4D3sYdMtNHzcgLT4jGPWci4gz+YAHU6P+M7zBb/+9U 59 | C/Ma6OALg/ZAum9mO3kUCQYLqR5MBNtwgMn3kpTxgdEbQo3bCx9cj/qsb+tnxZmE 60 | ZRsba/Wt//9iWx9SY07D/0+1V4j6Me0IjSve65sf0Bf/BPUziGI5tgjOp6Dcr85p 61 | foj3Sy77DSz/gBqgcvxrIF3L/f2lahHFgpswsH9D+iax77mydAE391DCp9JuSSHZ 62 | 9ScAEQEAAYkBHwQYAQoACQUCU9/Q9QIbDAAKCRABaYH3IZChsf5bB/9/KudG5ZDs 63 | mrQzndVwA+VkY4olvf6M/H5We4AdOkhOOp5jNSIgOPXOgwKd11JYHgVofLnFMGCI 64 | fMoxqeG2rVLb5MZaEVTYt4F0Kks3xzM403Kh//iA7YxdLCAUEsST60ewd00zmMvB 65 | LWS7uyXLpO0dPzkz6kCjNlyXDE7J+hvoK3iRtqFruXS4VbtcJ/M88EF/kqXNM71G 66 | bUoUqKtroRgKjG25DAifuuhiTu7I5fPgIZWbAXHbZblpTXJ4FdPC4XOBas6wq1CY 67 | /cvSChUcpWdANTIyDu2wXaW2eY6+Ff9QlhwpWycC6MA1AOCUImnfAf5SXVVvRmZK 68 | rf6b7dGhhdAJ 69 | =yR8T 70 | -----END PGP PUBLIC KEY BLOCK----- 71 | $$); 72 | -------------------------------------------------------------------------------- /demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PGOPTIONS="-c client_min_messages=ERROR" 3 | 4 | # Let's create two databases, one for TESTBANK001 and another for TESTBANK002. 5 | # This will allow us to see exactly what is being written to each one of the databases. 6 | 7 | dropdb TESTBANK001 8 | dropdb TESTBANK002 9 | createdb TESTBANK001 10 | createdb TESTBANK002 11 | 12 | psql -X -q -f install.sql -d TESTBANK001 13 | psql -X -q -f install.sql -d TESTBANK002 14 | psql -X -q -f testdata/test-banks.sql -d TESTBANK001 15 | psql -X -q -f testdata/test-banks.sql -d TESTBANK002 16 | psql -X -q -f testdata/test-publickeyrings.sql -d TESTBANK001 17 | psql -X -q -f testdata/test-publickeyrings.sql -d TESTBANK002 18 | psql -X -q -f testdata/test-secretkeyring-TESTBANK001.sql -d TESTBANK001 19 | psql -X -q -f testdata/test-secretkeyring-TESTBANK002.sql -d TESTBANK002 20 | 21 | # Use Create_Message() to encrypt/sign a new message. 22 | 23 | MessageID=$(psql -P format=unaligned -t -c " 24 | SELECT MessageID FROM Create_Message( 25 | _MessageType := 'text/plain', 26 | _Plaintext := 'This is a secret message from TESTBANK001 to TESTBANK002', 27 | _FromBankID := 'TESTBANK001', 28 | _ToBankID := 'TESTBANK002' 29 | ); 30 | " -d TESTBANK001) 31 | 32 | # A unique MessageID is returned, which is a SHA512 hash of the encrypted/signed 33 | # data stored in Messages.Cipherdata in which table the MessageID is the primary key. 34 | # The Plaintext is written to Files.Plaintext, where the Files.FileID is a SHA512 35 | # hash of the Plaintext and is also the primary key of the same table. 36 | 37 | echo "MessageID: '$MessageID'" 38 | echo 39 | 40 | # Use Get_Message() to get the Messages.Cipherdata in ASCII armored format. 41 | # This is the data we will transmit to the "to bank" in this case TESTBANK002. 42 | 43 | Ciphertext=$(psql -P format=unaligned -t -c " 44 | SELECT Ciphertext FROM Get_Message( 45 | _MessageID := '$MessageID' 46 | ); 47 | " -d TESTBANK001) 48 | 49 | echo "Ciphertext:" 50 | echo "$Ciphertext" 51 | echo 52 | 53 | # The "to bank" receiving the message should make the Receive_Message() 54 | # function accessible via HTTPS POST, allowing the "from bank" to call 55 | # the function with the Ciphertext returned by Get_Message(). 56 | 57 | DeliveryReceipt=$(psql -P format=unaligned -t -c " 58 | SELECT DeliveryReceipt FROM Receive_Message( 59 | _Ciphertext := '$Ciphertext' 60 | ); 61 | " -d TESTBANK002) 62 | 63 | echo "DeliveryReceipt:" 64 | echo "$DeliveryReceipt" 65 | 66 | # The Receive_Message() returns a DeliveryReceipt which is a proof that 67 | # the "to bank" received the message, and consists of a SHA512 hash 68 | # of the plaintext message, encrypted/signed by the "to bank", 69 | # which allows the "from bank" to decrypt/verify the signature 70 | # of the DeliveryReceipt, to be sure the message was delivered. 71 | 72 | echo "Decode_Delivery_Receipt(DeliveryReceipt):" 73 | 74 | psql -c " 75 | SELECT FileID, FromBankID, ToBankID FROM Decode_Delivery_Receipt( 76 | _DeliveryReceipt := '$DeliveryReceipt' 77 | ); 78 | " -d TESTBANK001 79 | 80 | # Since the FileID is a SHA512 hash of the message, we know it will be: 81 | # aac8c8a1bfc8e6ae97c8aaa132472f39cc2497db23bfae424b6ee52e4e34a92574e34f6d0cd713cdd630da72a4aa151030b3c1b3745ca393af119e45b929c943 82 | # as the message in this case was 'This is a secret message from TESTBANK001 to TESTBANK002'. 83 | 84 | # Now, let's look at the content of the tables Files and Messages in the two databases TESTBANK001 and TESTBANK002: 85 | 86 | echo "TESTBANK001.Files:" 87 | psql -P expanded -c "SELECT * FROM Files" -d TESTBANK001 88 | 89 | echo "TESTBANK002.Files:" 90 | psql -P expanded -c "SELECT * FROM Files" -d TESTBANK002 91 | 92 | # As we can see, the message has been transferred successfully from TESTBANK001 to TESTBANK002. 93 | 94 | # Now, let's look at Messages in both databases: 95 | 96 | echo "TESTBANK001.Messages:" 97 | psql -P expanded -c "SELECT * FROM Messages" -d TESTBANK001 98 | 99 | echo "TESTBANK002.Messages:" 100 | psql -P expanded -c "SELECT * FROM Messages" -d TESTBANK002 101 | 102 | -------------------------------------------------------------------------------- /doc/call_graph/t3225048.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225048e3224946 14 | 15 | 16 | digest 17 | 18 | 19 | 20 | 21 | t3225048e3224947 22 | 23 | 24 | digest 25 | 26 | 27 | 28 | 29 | t3225048e3224977 30 | 31 | 32 | pgp_pub_encrypt_sign_bytea 33 | 34 | 35 | 36 | 37 | t3225048e3225046 38 | 39 | 40 | encrypt_sign 41 | 42 | 43 | 44 | 45 | t3225048e3225046->t3225048e3224977 46 | 47 | 48 | 49 | 50 | t3225048e3225048 51 | 52 | 53 | create_message 54 | 55 | 56 | 57 | 58 | t3225048e3225048->t3225048e3224946 59 | 60 | 61 | 62 | 63 | t3225048e3225048->t3225048e3224947 64 | 65 | 66 | 67 | 68 | t3225048e3225048->t3225048e3225046 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /doc/rationale.md: -------------------------------------------------------------------------------- 1 | ## Why? 2 | 3 | All banks currently communicate with each other over the SWIFT network. 4 | The message types sent over SWIFT will probably remain the same for a long time, such as FIN MT-files or FileAct ISO20022 XML-files. 5 | Many banks depend on legacy systems and cannot easily change the file formats they use to communicate with each other. 6 | 7 | SWIFT is a centralized system, which basically is a gigantic mailserver in which each bank has a mail account with a mail address, a so called BIC, such as "TRLYSESSXXX". 8 | SWIFT is highly secure and guarantees the plaintext data you send over the network won't be read by anyone else than potentially SWIFT themselves, 9 | and also guarantees the message a bank receives originates from the bank who claims to have sent the message. 10 | 11 | We take for granted emails are free of charge. But SWIFT messages are not free of charge, which is perfectly understandable, as it's a centralized system, and they must make money to keep the platform up and running. 12 | Compare this with for instance the email protocol SMTP, which is by its design decentralized, meaning there is no single giant mailserver in the middle responsible for delivering all emails between its users. There are some really big ones though, like Gmail. 13 | 14 | The fees charged by SWIFT was the main reason why this project took off in the first place. 15 | We wanted a way to communicate with other banks in a more efficient way without any fees to do something as simple as sending a message from A to B. 16 | 17 | The decentralized design of BankAPI protocol ensures no one controls it, no one owns it, no one can shut it down, just like the Internet. 18 | The un-innovative design of BankAPI protocol ensures no one can criticize it, as there is nothing new invented, it's just a combination of existing well proven technologies. 19 | 20 | Imagine if all mail in the world would be sent to a single central SMTP mailserver in Belgium, and from there delivered to the addressee. 21 | That would be horrible from an efficiency perspective. 22 | It would be slower, more costly, and just plain stupid, than the obvious idea, to send the messages as fast and cheap as possible from A to B. 23 | Let's imagine banks would send their messages between each other via the insecure Internet instead of over the secure SWIFT network. 24 | How could B be certain the message sent from A really comes from B? And what if the message is confidential? 25 | We all know the Internet is insecure, data sent can be "sniffed" by all the routers between A and B. 26 | We all know IP-addresses on the Internet can be "spoofed", so how can B really know the message came from A? 27 | 28 | Internet and Swift was invented more than 40 years ago about the same time as another invention called RSA (public key cryptography). But RSA is just an algorithm to do encryption/decryption using public/secret keys. There was still not a widely accepted standard to solve the problem of communicating securely until the OpenPGP standard, defined by RFC 4880, released in 2007. 29 | 30 | A good thing with OpenPGP is it doesn't say anything about what you can send in a message. 31 | A message can be a binary file, a text file a message or anything you like to send from A to B. 32 | 33 | OpenPGP could even be used to send the types of files exchange between banks. 34 | Many banks are probably still using 7-bit file formats, but even those are supported. 35 | 36 | So given we have the Internet and a widely accepted standard to do secure messaging over an insecure network, 37 | could we somehow come up with a drop-in replacement to let banks send their messages between each other over the Internet instead of SWIFT? 38 | 39 | The one tricky part is how they would exchange public keys. If they would send them by email, the emails could have been spoofed. 40 | If they would exchange public keys in person by visiting each others bank headquarters, that would be inefficient. 41 | 42 | It turns out SWIFT is perfectly suited for the task of exchanging public keys. 43 | If bank A sends their public keys to bank B as a normal SWIFT text message, bank B will know it really comes from A. 44 | Bank B will also be able to later on provide proof to others that bank A really did send them that particular public key, as SWIFT messages are archieved. 45 | That also means Bank B will be able to prove all messages sent over the Internet from bank A really came from bank A. 46 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/r3225023.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | files 11 | 12 | 13 | 3225050 14 | 15 | 16 | receive_message 17 | 18 | 19 | 20 | 21 | 3225023 22 | 23 | files 24 | 25 | sequential scans: 0 26 | 27 | avg seq tuples: 0 28 | 29 | index scans: 11 30 | 31 | avg idx tuples: 0.91 32 | 33 | inserted rows: 1 34 | 35 | updated rows: 0 36 | 37 | deleted rows: 0 38 | 39 | 40 | 3225050->3225023:name 41 | 42 | 43 | 44 | 45 | 3225052 46 | 47 | 48 | decode_delivery_receipt 49 | 50 | 51 | 52 | 53 | 3225052->3225023:name 54 | 55 | 56 | 57 | 58 | 3225048 59 | 60 | 61 | create_message 62 | 63 | 64 | 65 | 66 | 3225048->3225023:name 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225052.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225052 14 | 15 | decode_delivery_receipt 16 | 17 | 18 | 3224995 19 | 20 | 21 | dearmor 22 | 23 | 24 | 25 | 26 | 3225052->3224995 27 | 28 | 29 | 30 | 31 | 3225047 32 | 33 | 34 | decrypt_verify 35 | 36 | 37 | 38 | 39 | 3225052->3225047 40 | 41 | 42 | 43 | 44 | 3224986 45 | 46 | 47 | pgp_pub_decrypt_verify_bytea 48 | 49 | 50 | 51 | 52 | 3225047->3224986 53 | 54 | 55 | 56 | 57 | 3224992 58 | 59 | 60 | pgp_pub_signature_keys 61 | 62 | 63 | 64 | 65 | 3225047->3224992 66 | 67 | 68 | 69 | 70 | 3224989 71 | 72 | 73 | pgp_key_id 74 | 75 | 76 | 77 | 78 | 3225047->3224989 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /doc/call_graph/t3225052.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225052e3224986 14 | 15 | 16 | pgp_pub_decrypt_verify_bytea 17 | 18 | 19 | 20 | 21 | t3225052e3224989 22 | 23 | 24 | pgp_key_id 25 | 26 | 27 | 28 | 29 | t3225052e3224992 30 | 31 | 32 | pgp_pub_signature_keys 33 | 34 | 35 | 36 | 37 | t3225052e3224995 38 | 39 | 40 | dearmor 41 | 42 | 43 | 44 | 45 | t3225052e3225047 46 | 47 | 48 | decrypt_verify 49 | 50 | 51 | 52 | 53 | t3225052e3225047->t3225052e3224986 54 | 55 | 56 | 57 | 58 | t3225052e3225047->t3225052e3224989 59 | 60 | 61 | 62 | 63 | t3225052e3225047->t3225052e3224992 64 | 65 | 66 | 67 | 68 | t3225052e3225052 69 | 70 | 71 | decode_delivery_receipt 72 | 73 | 74 | 75 | 76 | t3225052e3225052->t3225052e3224995 77 | 78 | 79 | 80 | 81 | t3225052e3225052->t3225052e3225047 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225047.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225052 14 | 15 | 16 | decode_delivery_receipt 17 | 18 | 19 | 20 | 21 | 3225047 22 | 23 | decrypt_verify 24 | 25 | 26 | 3225052->3225047 27 | 28 | 29 | 30 | 31 | 3224986 32 | 33 | 34 | pgp_pub_decrypt_verify_bytea 35 | 36 | 37 | 38 | 39 | 3225047->3224986 40 | 41 | 42 | 43 | 44 | 3224992 45 | 46 | 47 | pgp_pub_signature_keys 48 | 49 | 50 | 51 | 52 | 3225047->3224992 53 | 54 | 55 | 56 | 57 | 3224989 58 | 59 | 60 | pgp_key_id 61 | 62 | 63 | 64 | 65 | 3225047->3224989 66 | 67 | 68 | 69 | 70 | 3225050 71 | 72 | 73 | receive_message 74 | 75 | 76 | 77 | 78 | 3225050->3225047 79 | 80 | 81 | 82 | 83 | 3225051 84 | 85 | 86 | read_message 87 | 88 | 89 | 90 | 91 | 3225051->3225047 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/r3225032.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | messages 11 | 12 | 13 | 3225048 14 | 15 | 16 | create_message 17 | 18 | 19 | 20 | 21 | 3225032 22 | 23 | messages 24 | 25 | sequential scans: 8 26 | 27 | avg seq tuples: 0.88 28 | 29 | index scans: 9 30 | 31 | avg idx tuples: 1.00 32 | 33 | inserted rows: 1 34 | 35 | updated rows: 2 36 | 37 | deleted rows: 0 38 | 39 | 40 | 3225048->3225032:name 41 | 42 | 43 | 44 | 45 | 3225049 46 | 47 | 48 | get_message 49 | 50 | 51 | 52 | 53 | 3225049->3225032:name 54 | 55 | 56 | 57 | 58 | 3225052 59 | 60 | 61 | decode_delivery_receipt 62 | 63 | 64 | 65 | 66 | 3225052->3225032:name 67 | 68 | 69 | 70 | 71 | 3225051 72 | 73 | 74 | read_message 75 | 76 | 77 | 78 | 79 | 3225051->3225032:name 80 | 81 | 82 | 83 | 84 | 3225050 85 | 86 | 87 | receive_message 88 | 89 | 90 | 91 | 92 | 3225050->3225032:name 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225051.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225051 14 | 15 | read_message 16 | 17 | 18 | 3224995 19 | 20 | 21 | dearmor 22 | 23 | 24 | 25 | 26 | 3225051->3224995 27 | 28 | 29 | 30 | 31 | 3225049 32 | 33 | 34 | get_message 35 | 36 | 37 | 38 | 39 | 3225051->3225049 40 | 41 | 42 | 43 | 44 | 3225047 45 | 46 | 47 | decrypt_verify 48 | 49 | 50 | 51 | 52 | 3225051->3225047 53 | 54 | 55 | 56 | 57 | 3224994 58 | 59 | 60 | armor 61 | 62 | 63 | 64 | 65 | 3225049->3224994 66 | 67 | 68 | 69 | 70 | 3224986 71 | 72 | 73 | pgp_pub_decrypt_verify_bytea 74 | 75 | 76 | 77 | 78 | 3225047->3224986 79 | 80 | 81 | 82 | 83 | 3224992 84 | 85 | 86 | pgp_pub_signature_keys 87 | 88 | 89 | 90 | 91 | 3225047->3224992 92 | 93 | 94 | 95 | 96 | 3224989 97 | 98 | 99 | pgp_key_id 100 | 101 | 102 | 103 | 104 | 3225047->3224989 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /doc/call_graph/t3225051.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225051e3224986 14 | 15 | 16 | pgp_pub_decrypt_verify_bytea 17 | 18 | 19 | 20 | 21 | t3225051e3224989 22 | 23 | 24 | pgp_key_id 25 | 26 | 27 | 28 | 29 | t3225051e3224992 30 | 31 | 32 | pgp_pub_signature_keys 33 | 34 | 35 | 36 | 37 | t3225051e3224994 38 | 39 | 40 | armor 41 | 42 | 43 | 44 | 45 | t3225051e3224995 46 | 47 | 48 | dearmor 49 | 50 | 51 | 52 | 53 | t3225051e3225047 54 | 55 | 56 | decrypt_verify 57 | 58 | 59 | 60 | 61 | t3225051e3225047->t3225051e3224986 62 | 63 | 64 | 65 | 66 | t3225051e3225047->t3225051e3224989 67 | 68 | 69 | 70 | 71 | t3225051e3225047->t3225051e3224992 72 | 73 | 74 | 75 | 76 | t3225051e3225049 77 | 78 | 79 | get_message 80 | 81 | 82 | 83 | 84 | t3225051e3225049->t3225051e3224994 85 | 86 | 87 | 88 | 89 | t3225051e3225051 90 | 91 | 92 | read_message 93 | 94 | 95 | 96 | 97 | t3225051e3225051->t3225051e3224995 98 | 99 | 100 | 101 | 102 | t3225051e3225051->t3225051e3225047 103 | 104 | 105 | 106 | 107 | t3225051e3225051->t3225051e3225049 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /doc/call_graph/tableusage/r3225005.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | keys 11 | 12 | 13 | 3225050 14 | 15 | 16 | receive_message 17 | 18 | 19 | 20 | 21 | 3225005 22 | 23 | keys 24 | 25 | sequential scans: 8 26 | 27 | avg seq tuples: 2.00 28 | 29 | index scans: 22 30 | 31 | avg idx tuples: 1.00 32 | 33 | inserted rows: 0 34 | 35 | updated rows: 0 36 | 37 | deleted rows: 0 38 | 39 | 40 | 3225050->3225005:name 41 | 42 | 43 | 44 | 45 | 3225046 46 | 47 | 48 | encrypt_sign 49 | 50 | 51 | 52 | 53 | 3225046->3225005:name 54 | 55 | 56 | 57 | 58 | 3225052 59 | 60 | 61 | decode_delivery_receipt 62 | 63 | 64 | 65 | 66 | 3225052->3225005:name 67 | 68 | 69 | 70 | 71 | 3225047 72 | 73 | 74 | decrypt_verify 75 | 76 | 77 | 78 | 79 | 3225047->3225005:name 80 | 81 | 82 | 83 | 84 | 3225048 85 | 86 | 87 | create_message 88 | 89 | 90 | 91 | 92 | 3225048->3225005:name 93 | 94 | 95 | 96 | 97 | 3225051 98 | 99 | 100 | read_message 101 | 102 | 103 | 104 | 105 | 3225051->3225005:name 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /doc/call_graph/perfunction/3225050.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | 3225050 14 | 15 | receive_message 16 | 17 | 18 | 3224994 19 | 20 | 21 | armor 22 | 23 | 24 | 25 | 26 | 3225050->3224994 27 | 28 | 29 | 30 | 31 | 3225046 32 | 33 | 34 | encrypt_sign 35 | 36 | 37 | 38 | 39 | 3225050->3225046 40 | 41 | 42 | 43 | 44 | 3224995 45 | 46 | 47 | dearmor 48 | 49 | 50 | 51 | 52 | 3225050->3224995 53 | 54 | 55 | 56 | 57 | 3224946 58 | 59 | 60 | digest 61 | 62 | 63 | 64 | 65 | 3225050->3224946 66 | 67 | 68 | 69 | 70 | 3224947 71 | 72 | 73 | digest 74 | 75 | 76 | 77 | 78 | 3225050->3224947 79 | 80 | 81 | 82 | 83 | 3225047 84 | 85 | 86 | decrypt_verify 87 | 88 | 89 | 90 | 91 | 3225050->3225047 92 | 93 | 94 | 95 | 96 | 3224977 97 | 98 | 99 | pgp_pub_encrypt_sign_bytea 100 | 101 | 102 | 103 | 104 | 3225046->3224977 105 | 106 | 107 | 108 | 109 | 3224986 110 | 111 | 112 | pgp_pub_decrypt_verify_bytea 113 | 114 | 115 | 116 | 117 | 3225047->3224986 118 | 119 | 120 | 121 | 122 | 3224992 123 | 124 | 125 | pgp_pub_signature_keys 126 | 127 | 128 | 129 | 130 | 3225047->3224992 131 | 132 | 133 | 134 | 135 | 3224989 136 | 137 | 138 | pgp_key_id 139 | 140 | 141 | 142 | 143 | 3225047->3224989 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /doc/call_graph/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Call graphs 4 | 5 | 6 | 7 | 8 | 9 | 10 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 122 | 123 |
receive_message
2 calls140.57 ms total70.28 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
read-write tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
messages0031.00010
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
keys42.0081.00000
files0021.00000

View table information
read_message
1 calls60.34 ms total60.34 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
keys12.0031.00000
messages0011.00000

View table information
decode_delivery_receipt
1 calls57.71 ms total57.71 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
read-write tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
messages11.0000010
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
keys12.0031.00000
files0011.00000

View table information
create_message
7 calls24.37 ms total3.48 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
read-write tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
files0080.88100
messages70.8600100
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
keys0041.00000

View table information
decrypt_verify
2 calls128.75 ms total64.38 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
90 | 91 | 92 | 93 | 94 | 95 |
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
keys22.0021.00000

View table information
get_message
5 calls0.77 ms total0.15 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
103 | 104 | 105 | 106 | 107 | 108 |
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
messages0051.00000

View table information
encrypt_sign
1 calls19.75 ms total19.75 ms averageFirst call
2014-08-06 20:44:38
Last call
2014-08-06 20:44:38
No calls today
No calls in the current hour
No calls in the previous hour
116 | 117 | 118 | 119 | 120 | 121 |
read-only tablesseq_scanseq_tup_readidx_scanidx_tup_readn_tup_insn_tup_updn_tup_del
keys0021.00000

View table information
124 | 125 | -------------------------------------------------------------------------------- /doc/call_graph/t3225050.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | graph1 11 | 12 | 13 | t3225050e3224946 14 | 15 | 16 | digest 17 | 18 | 19 | 20 | 21 | t3225050e3224947 22 | 23 | 24 | digest 25 | 26 | 27 | 28 | 29 | t3225050e3224977 30 | 31 | 32 | pgp_pub_encrypt_sign_bytea 33 | 34 | 35 | 36 | 37 | t3225050e3224986 38 | 39 | 40 | pgp_pub_decrypt_verify_bytea 41 | 42 | 43 | 44 | 45 | t3225050e3224989 46 | 47 | 48 | pgp_key_id 49 | 50 | 51 | 52 | 53 | t3225050e3224992 54 | 55 | 56 | pgp_pub_signature_keys 57 | 58 | 59 | 60 | 61 | t3225050e3224994 62 | 63 | 64 | armor 65 | 66 | 67 | 68 | 69 | t3225050e3224995 70 | 71 | 72 | dearmor 73 | 74 | 75 | 76 | 77 | t3225050e3225046 78 | 79 | 80 | encrypt_sign 81 | 82 | 83 | 84 | 85 | t3225050e3225046->t3225050e3224977 86 | 87 | 88 | 89 | 90 | t3225050e3225047 91 | 92 | 93 | decrypt_verify 94 | 95 | 96 | 97 | 98 | t3225050e3225047->t3225050e3224986 99 | 100 | 101 | 102 | 103 | t3225050e3225047->t3225050e3224989 104 | 105 | 106 | 107 | 108 | t3225050e3225047->t3225050e3224992 109 | 110 | 111 | 112 | 113 | t3225050e3225050 114 | 115 | 116 | receive_message 117 | 118 | 119 | 120 | 121 | t3225050e3225050->t3225050e3224946 122 | 123 | 124 | 125 | 126 | t3225050e3225050->t3225050e3224947 127 | 128 | 129 | 130 | 131 | t3225050e3225050->t3225050e3224994 132 | 133 | 134 | 135 | 136 | t3225050e3225050->t3225050e3224995 137 | 138 | 139 | 140 | 141 | t3225050e3225050->t3225050e3225046 142 | 143 | 144 | 145 | 146 | t3225050e3225050->t3225050e3225047 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /test.sql: -------------------------------------------------------------------------------- 1 | SELECT MessageID FROM Create_Message( 2 | _Plaintext := 'Hello world', 3 | _MessageType := 'text/plain', 4 | _FromBankID := 'TESTBANK001', 5 | _ToBankID := 'TESTBANK002' 6 | ); 7 | -- messageid 8 | -- ---------------------------------------------------------------------------------------------------------------------------------- 9 | -- f9df7006a9b8617462ccc34125e02c7d34d6c1cf0773f9c9e23394c2eef6383c477a19beb515daeb669d812588bcf73110ef04796ce71d7ba8bbd53977fcfee7 10 | -- (1 row) 11 | 12 | SELECT Ciphertext FROM Get_Message( 13 | _MessageID := Create_Message('Hello world','text/plain','TESTBANK001','TESTBANK002') 14 | ); 15 | -- ciphertext 16 | -- ------------------------------------------------------------------------------ 17 | -- -----BEGIN PGP MESSAGE----- + 18 | -- + 19 | -- wcBMA6i1kJX5yWCQAQf/cfbi6O1vQ8OtUKjkufGljQPPFxi26/Ac6+esQHyzkkY9c2cjPWaRoTxK+ 20 | -- 4y3Qhw0RVek2nZCLZbvt8smjSqYwb5QpoegBhF9qaon1WxBfxWhRLYocEae+TRyccfPOM/SwxTXD+ 21 | -- l+ITsmm1KdKd38Poxet2BG60nHyiV8uciVi9HdjG95WMYxuWE/80dsGtc2+bAb+SVf5fpfxL5e2m+ 22 | -- /S+f0GoEyhgcndvfYkyXIPABrJK2TOiZ81vimZ5QxTHSzH2XaMj1uuMLE73vdvmIo8JUw5oilBSc+ 23 | -- 3IAgzX0dn7jyX4iFBAEUTJOesSbh6yCk9KNonAgSsQaEjmV68ESZQjWlIdLAqgGUMMZoFZsElfh7+ 24 | -- bTJnlDXdFOVx5rgzDF0581nFDkVYeqfnBuftNT4AgNeSnY+cadB+4sJzE7FA7Ifj7LSihMqneBjL+ 25 | -- Aw33hNI9YwuMHiP2bpGgwfbwNHB+qmkWkcd0EVxfZMdjdZGl0zqFkR9r34m9c0p/1UcHXyzLp7jH+ 26 | -- NgDB38V/lcZRAfoekiO2p+9YKlNjlITyEiUW3RrX9BMcB/nIy6nbj1BZ41UqGQZaC6L9SX910sYD+ 27 | -- aaL2ZnyHxBNYytb26MUFTBSCGSMVf8NsEvO71cHQmp10WhYSGlhlky0FBC1adoK/GegQDeRiAxZe+ 28 | -- s2tl3tH8mw4wJ715R2F/pVTdUQ4hDcaEkiEaDT/N0Qs99Ufr7x92GnKewwcAfEorD6lveNtNcvT8+ 29 | -- FkvEM1lLD7G6p0k3r86Z2w4/d7kSF34x15f3dOrN40bwhh86ufgQef0dCWysPXhuIW15CDIgRM1k+ 30 | -- mPYyferGxgb6 + 31 | -- =eslQ + 32 | -- -----END PGP MESSAGE----- + 33 | -- 34 | -- (1 row) 35 | 36 | SELECT Plaintext, FromBankID, ToBankID FROM Read_Message( 37 | _MessageID := Create_Message('Hello world','text/plain','TESTBANK001','TESTBANK002') 38 | ); 39 | -- plaintext | frombankid | tobankid 40 | -- -------------+-------------+------------- 41 | -- Hello world | TESTBANK001 | TESTBANK002 42 | -- (1 row) 43 | 44 | SELECT EncryptionKeyID, SignatureKeyID, Plaintext FROM Decrypt_Verify( 45 | _Cipherdata := dearmor(Get_Message(Create_Message('Hello world','text/plain','TESTBANK001','TESTBANK002'))) 46 | ); 47 | -- encryptionkeyid | signaturekeyid | plaintext 48 | -- ------------------+------------------+------------- 49 | -- A8B59095F9C96090 | 4F5C0E03014223B9 | Hello world 50 | -- (1 row) 51 | 52 | 53 | SELECT FileID, FromBankID, ToBankID FROM Decode_Delivery_Receipt( 54 | _DeliveryReceipt := Receive_Message(Get_Message(Create_Message('Hello world','text/plain','TESTBANK001','TESTBANK002'))) 55 | ); 56 | -- fileid | frombankid | tobankid 57 | -- ----------------------------------------------------------------------------------------------------------------------------------+-------------+------------- 58 | -- b7f783baed8297f0db917462184ff4f08e69c2d5e5f79a942600f9725f58ce1f29c18139bf80b06c0fff2bdd34738452ecf40c488c22a7e3d80cdf6f9c1c0d47 | TESTBANK001 | TESTBANK002 59 | -- (1 row) 60 | 61 | SELECT EncryptionKeyID, SignatureKeyID, Plaintext FROM Decrypt_Verify( 62 | _Cipherdata := dearmor(Get_Message(Create_Message('Hello world','text/plain','TESTBANK001','TESTBANK002'))) 63 | ); 64 | -- encryptionkeyid | signaturekeyid | plaintext 65 | -- ------------------+------------------+------------- 66 | -- A8B59095F9C96090 | 4F5C0E03014223B9 | Hello world 67 | -- (1 row) 68 | 69 | SELECT Cipherdata FROM Encrypt_Sign( 70 | _Plaintext := 'Hello world', 71 | _EncryptionKeyID := (SELECT SubKeyID FROM Keys WHERE BankID = 'TESTBANK001'), 72 | _SignatureKeyID := (SELECT MainKeyID FROM Keys WHERE BankID = 'TESTBANK001') 73 | ); 74 | -- cipherdata 75 | -- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 76 | -- \xc1c04c033395abb62bd7c2ed0107ff48da45cd7f812535e7fd01a7004907114245d3b7538e995e223b1a687f87853b42d471e60dc0c8bc9314d39f509e900b1a90bd77fc8c604199d905feb08acd27393f7a4d14f8153e716ee9d44eb335abb563b30494a4d604781c066e4768dee4978c79d805236c3ff90c4cc756bb37ea9e0c347558460fd9ef66969d5b8571712f32396422c298923b5fb62a0eb4c1554dc6ee8374b1ed1610e1bd1f29f4cddce65fa08777285e916c8c34182072fe0c6e325ace2064bca6d75011814ed69fc9a8b1acaecfe8e6f5e77bc38e8018a8f43aeb8356012e4a9a39aea6e0f8396d6264eaa973737c98d1d2259f31f7339c72b14520634c66fc014f86f8c27641c1d3d2c0aa0154f8fbc743e101e0035f8ced17e2c6406cce3c05695945d24870afe803944292499d445771b38f278174553cb4cb9e90137828ab99c9c16b35a0326c758c4e0db7bf7008658eebd6ce2680ff085950656179edc4347d7fc00ef4583b760f4e398cc25696772f65e839f4b014a9155249899776cbe683281e526aced2e9c79574c6cb8eb061cd9327e614945fef7d7909bb71b5b8787f64773e616a5220b6f7b8b677dab4d7f06999f1f147588104dd223910f2f778f393121af7187631ff61392d1c91e1be11f3b4667a3b6e35b12ca1f658251dd190675704de04bada707bb6facb0a304266a4bd7879c4e21c4cc8d589b3c41bb9d1bd116abc5982e50dc46819345d9765dc2c7f2fcdaf38854da47c9b2e35b98bd4dbc36c8b64dc6ccf92904365d7190ef8096fb756917fef6d0e11c04f1f977aab2239a5fd307a7ae14e7f6e09a9687889eb721f9519521814f8e27fcdcbf9e7be8f5e6cc97bc1d7df2ceb257245930f34baa363 77 | -- (1 row) 78 | 79 | SELECT DeliveryReceipt FROM Receive_Message( 80 | _Ciphertext := Get_Message(Create_Message('Hello world','text/plain','TESTBANK001','TESTBANK002')) 81 | ); 82 | 83 | -- deliveryreceipt 84 | -- ------------------------------------------------------------------------------ 85 | -- -----BEGIN PGP MESSAGE----- + 86 | -- + 87 | -- wcBMAzOVq7Yr18LtAQf/YbzuQdGbTVz9B6ZanLnVj4W/4gowMzEwNUC9BCw88yqNN2hTzdb0JvIz+ 88 | -- I3sBmLZ8tC9Hf3EQvI+a6tluMJG4n2Ldi7BW9SHqaI82b0EL0YoKfdAvIQ+lpgikzUnjFkBrJDfA+ 89 | -- Jv7HqLuyrT5bAimWtWV87CFygkwYAca52RE3NSE1mY27JiYZ9+DMfInWDlmB2X2j4KCVJAE6BQNj+ 90 | -- MLfzDuA4ADDsJW7+TWHhHS0uX2BqeLf5GE8nN/oECMLgT7u+MsTdSXqmcHxOrEk/m0jM3GR08ZPu+ 91 | -- jZ5S9NYwvBgQuUDlUoUWBC0WcrAzbzjKSGcxqWA1UmX0FCVChxyTvQZDf9LBHwFOdLe4yMWWPQFg+ 92 | -- 5hrlAN5beCAV6rVL5p4m+2ri368XwJnNxGpLGh36SooeGPxXzTwfr3TAliQP9WLZED8FD5O5qnQ3+ 93 | -- tYvNvGxtxaeyNG+LYbPOKEpr5WBUrfXuwoXVzuiMHecN5vOxhdyKWZCdvvYxToFgyWoOJRqDoIMH+ 94 | -- EO22rJRgwqygu0FWl5TGiKzmx7HevxTHiUovO5SWLT+ZQ8oDQBHjv1MrSshQ1Me+di/QmtPYxSKi+ 95 | -- T8KtJV7MZBrsViZj+sDcBuL8AAxsGWkb+wD62jyHJtB7PCjdplEQ53Tb6D+EDX+iCsgH1099QDer+ 96 | -- Eo8tsG22GSTfWnponi3tmixgzenSKEVqNDoevyblhYsKoOwKWkLYYdh9Widgs+mwSkiASOwggM5W+ 97 | -- 7k0nbh1jSblVb/MMFM3DfibnoXiRIf34lZh63OK43yv7r5yBZI4OJJPawnDtYHk5iDmgBAEWuX8k+ 98 | -- qJDydR+7cBQ9RN9wxQsCsBGHMqPnxEzoJWeznE7UtsuxeGGHAGWJSxj7rxUW1+6659Udyr0/Hk7Q+ 99 | -- eqfQuuf5PylR9w1IMq/3s3ug7O7/dIS4eVB+4fLJmhgBC+3pNhH8+cKvcNnDAJ0uFKKaAYDdLI/t+ 100 | -- xYaw9L9kxdFqY5L3 + 101 | -- =wS1a + 102 | -- -----END PGP MESSAGE----- + 103 | -- 104 | -- (1 row) 105 | 106 | 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BankAPI 2 | 3 | ## What is it? 4 | 5 | BankAPI is a secure decentralized messaging system to send files/messages between banks and other types of financial institutions. 6 | 7 | There is a reference implementation of the protocol which can be used off-the-shelf, which is production grade quality and is not only for testing and demonstration, although it fulfils those two roles as well. 8 | 9 | The BankAPI protocol relies on OpenPGP (RFC 4880) + SHA512 + HTTPS. That's it, there is nothing more to it. 10 | 11 | BankAPI is only a transmission protocol, and makes no assumptions of what kind of messages or file types banks will want to transfer. 12 | 13 | ## Key exchange 14 | 15 | ![layout](https://raw.githubusercontent.com/trustly/bankapi/master/doc/BankAPI%20Key%20Exchange.png) 16 | 17 | ## Protocol design 18 | 19 | ![layout](https://raw.githubusercontent.com/trustly/bankapi/master/doc/BankAPI%20Protocol%20Design.png) 20 | 21 | The protocol imposes certain restrictions on the details and choices of 22 | algorithms used for generating the OpenPGP messages. The terminology used here 23 | conforms to the one specified in [RFC2119](http://tools.ietf.org/html/rfc2119). 24 | The details specific to OpenPGP are documented in 25 | [RFC4880](http://tools.ietf.org/html/rfc4880). 26 | 27 | 1. All OpenPGP messages **MUST** be symmetrically encrypted messages, where the 28 | session key is public-key encrypted. The _session key packet_ **MUST** 29 | contain the _Key ID_ of the key used to encrypt the message. All messages 30 | **MUST** contain a signature packet. Implementations **MAY** choose to 31 | generate one-pass signatures. 32 | 33 | 2. All parties **MUST** use a key length of at least 2048 bits for their RSA 34 | keys. Implementations **SHOULD** accept key sizes of 2048, 3072 and 4096 35 | bits. Using 4096-bit keys is **RECOMMENDED**. DSA and Elgamal **MUST NOT** 36 | be used. 37 | 38 | 3. The cipher algorithm **MUST** be one of AES128, AES192 and AES256. 39 | 40 | 4. If the OpenPGP message is a _compressed message_, the compression algorithm 41 | **MUST** be ZIP or ZLIB. 42 | 43 | 5. The OpenPGP message **MAY** be _integrity protected_ via the _Modification 44 | Detection Code packet_. 45 | 46 | 6. The _literal data_ inside the OpenPGP message **MUST** be marked to contain 47 | binary data. If a text or UTF-8 message is received, an implementation 48 | **MAY** choose to accept the message. An implementation **MUST NOT** 49 | convert any _canonical line endings_ to their local counterpart. 50 | 51 | 7. The signature packet and (if present) the one-pass signature packet **MUST** 52 | contain the _Key ID_ of the key used to sign the message. Implementations 53 | **SHOULD** only sign a message using a single key. The hash algorithm used 54 | for signatures **MUST** be SHA-512. 55 | 56 | 8. The _ASCII Armor_, including _Armor Headers_, **MUST** be valid UTF-8. 57 | 58 | 9. The _Armor Header_ "Comment", **MUST** be set to the message type. 59 | 60 | ## System design 61 | 62 | ![layout](https://raw.githubusercontent.com/trustly/bankapi/master/doc/BankAPI%20System%20Design.png) 63 | 64 | The system is built on top of PostgreSQL and uses its pgcrypto contrib module. 65 | 66 | Messages are encrypted and signed using RSA public-key cryptography. 67 | 68 | Each bank generates a RSA key-pair consisting of a public and a secret key. 69 | 70 | The public keys and API URLs are exchanged between the banks. 71 | 72 | The task of exchanging public keys and API URLs is preferrably done over the 73 | SWIFT network, by sending them in a normal MT999 SWIFT FIN-message. This 74 | allows banks to trust the validity of the public key and API URL, as the origin 75 | of SWIFT messages can be trusted. 76 | 77 | ## Highlights 78 | 79 | - RSA-encryption 80 | - SHA512 81 | - JSON-RPC / HTTPS 82 | - Linux / PostgreSQL / Apache 83 | - Open source / MIT-license 84 | - Real-time request/response 85 | - Instant delivery receipt 86 | - Local archiving of files in PostgreSQL 87 | 88 | ## Overview of the interface 89 | 90 | To send a message, the sending bank encrypts and signs a message using 91 | Create\_Message(), and calls Get\_Message() to get the actual ciphertext 92 | content of the message. The ciphertext is then delivered to the receiving bank 93 | by calling its Receive\_Message() API method, accessible at the API URL 94 | provided by the receiving bank. The API implementation must adhere to the 95 | JSON-RPC standard and accept HTTPS POST. 96 | 97 | The Receive\_Message() function returns a _delivery receipt_, which is a 98 | cryptographic proof of the fact that the sender was able to verify the 99 | signature contained within the message against the receiving bank's public key. 100 | This allows the sender to be certain that the message was delivered and 101 | decrypted successfully by the recipient. 102 | 103 | The sending bank calls Decode\_Delivery\_Receipt() with the _delivery receipt_ 104 | as input to verify its validity. 105 | 106 | ## Database tables and columns 107 | 108 | ### Banks 109 | - BankID : Unique identifier for the bank, preferrably the SWIFT BIC (PRIMARY KEY) 110 | - Protocol : API procotol, example "https" 111 | - Host : API host, Internet domain name, example "bank.com" 112 | - Port : API port, Internet network port, example 443 113 | - Path : API path, example "/api" 114 | - Datestamp : Date/time when added to the table 115 | 116 | ### Files 117 | - FileID : SHA512 hash of the Plaintext (PRIMARY KEY) 118 | - Plaintext : The content of the file (UTF8) 119 | - Datestamp : Date/time when added to the table 120 | 121 | ### Keys 122 | - MainKeyID : Signature key (PRIMARY KEY) 123 | - SubKeyID : Encryption key 124 | - PublicKeyring : Contains public keys 125 | - SecretKeyring : Contains secret keys (only set for your own bank, NULL for others) 126 | - BankID : The BankID which the keys belong to 127 | - Datestamp : Date/time when added to the table 128 | - PrimaryKey : Only set to TRUE for one row per BankID, other keys are still valid, but this key is used when creating new messages 129 | 130 | ### Messages 131 | - MessageID : SHA512 hash of the Cipherdata (PRIMARY KEY) 132 | - FileID : The hash of the file send in the message 133 | - FromBankID : The bank who sent the message 134 | - ToBankID : The bank who received the message 135 | - Cipherdata : The encrypted and signed message 136 | - DeliveryReceipt : The encrypted and signed delivery receipt 137 | - Datestamp : The date/time when the message was created 138 | - Delivered : The date/time when the message was delivered 139 | 140 | ## Database functions 141 | 142 | ### Create\_Message(Plaintext, FromBankID, ToBankID) 143 | 144 | Create\_Message() takes the plaintext context of a file as input and prepares 145 | an encrypted _message_ signed by **FromBankID** which can be only decrypted by 146 | **ToBankID**. If the _message_ already exists, the existing data is reused. 147 | An error is returned if the _secret key_ of **FromBankID** or the _public key_ 148 | of **ToBankID** can not be located in the database. 149 | 150 | The return value is the _message id_ of the message. 151 | 152 | ### Get\_Message(MessageID) 153 | 154 | Given a _message id_ as input, Get\_Message() returns the ASCII armored PGP 155 | message. This data can then be HTTPS POSTed to the Receive\_Message API method 156 | of **ToBank**. 157 | 158 | ### Receive\_Message(Ciphertext text) 159 | 160 | Receive\_Message() decrypts and verifies the signature of the input _message_. 161 | The message's details are written to the _Messages_ table, and information 162 | about the file and its plaintext contents are written into the _Files_ table. 163 | This function will raise an error if: 164 | 165 | 1. The _secret key_ counterpart of the _public key_ used to encrypt the 166 | message is not present in the table _Keys_. 167 | 168 | 2. The _public key_ counterpart of the _secret key_ used to sign the message 169 | is not present in the table _Keys_. 170 | 171 | 3. The message is corrupt or the signature can not be verified. 172 | 173 | The return value is a _delivery receipt_, which should be then used as the 174 | contents of the response to the caller of the Receive\_Message API method. 175 | 176 | ### Decode\_Delivery\_Receipt(DeliveryReceipt text) 177 | 178 | Decode\_Delivery\_Receipt() decrypts and verifies the given _delivery receipt_, 179 | where the keys involved and the plaintext should match a row in Messages. If 180 | so, the Message has been successfully delivered, and the 181 | Messages.DeliveryReceipt and Messages.Delivered columns are set. 182 | 183 | ## Installing and building .deb packages for installation 184 | 185 | The repository includes scripts for building debian/ubuntu packages for the 186 | bankapi and the required pgcrypto extensions (in PostgreSQL) in order to run 187 | the code. 188 | 189 | If you are running a different version of PostgreSQL then 9.3 then replace the 190 | version in the commands below with the appropriate version. If you are running 191 | the default version in Debian you can omit the version extension of the 192 | packages names. 193 | 194 | - sudo apt-get -y build-dep postgresql-9.3 195 | - sudo apt-get -y install postgresql-server-dev-9.3 postgresql-9.3 apache2 python-psycopg2 python-requests 196 | - make 197 | 198 | This should produce two deb files, one for bankapi and one for the pgcrypto 199 | extension. 200 | 201 | ### bankapi-1.0.deb 202 | 203 | Package contains the command line tools for sending messages 204 | (/usr/bin/bankapi), SQL needed for creating the bankapi in postgres and 205 | installes a CGI as an endpoint for receving incoming communication messages. 206 | 207 | The installation of the module installs and activates the CGI script. It will 208 | be available under http://localhost/bankapi . 209 | 210 | The installation does not finish the installation of the database but leaves 211 | this as a manual task. Follow the following steps for a default database 212 | installation: 213 | 214 | - cd /usr/share/bankapi 215 | - sudo -u postgres createdb bankapi 216 | - sudo -u postgres psql --dbname=bankapi --single-transaction --no-psqlrc --file=install.sql 217 | - Make sure www-data user can connect to the bankapi database, this can for 218 | instance be done by adding the following line where appropriate in the active 219 | pg\_hba.conf file: local bankapi www-data peer 220 | - To optionally install the test bank data: sudo -u postgres psql --dbname=bankapi --single-transaction --no-psqlrc --file=testdata/index.sql 221 | 222 | ### postgresql-pgcrypto-openpgp-9.3.deb 223 | 224 | This is an extension to the pgcrypto PostgreSQL extension. The extension only 225 | installs with the extended functions, all functions normally in pgcrypto is 226 | still accessed from the standard pgcrypto extension. The extension is called 227 | pgcrypto\_openpgp. 228 | --------------------------------------------------------------------------------