diff --git a/create_db.sql b/create_db.sql index f90c631..efa9a83 100644 --- a/create_db.sql +++ b/create_db.sql @@ -12,7 +12,7 @@ CREATE TABLE cards ( k2_cmac_key CHAR(32) NOT NULL, k3 CHAR(32) NOT NULL, k4 CHAR(32) NOT NULL, - uid CHAR(14) NOT NULL, + uid VARCHAR(14) NOT NULL DEFAULT '', last_counter_value INTEGER NOT NULL, lnurlw_request_timeout_sec INT NOT NULL, lnurlw_enable CHAR(1) NOT NULL DEFAULT 'N', @@ -26,6 +26,7 @@ CREATE TABLE cards ( one_time_code_expiry TIMESTAMPTZ DEFAULT NOW() + INTERVAL '1 DAY', one_time_code_used CHAR(1) NOT NULL DEFAULT 'Y', allow_negative_balance CHAR(1) NOT NULL DEFAULT 'N', + wiped CHAR(1) NOT NULL DEFAULT 'N', PRIMARY KEY(card_id) ); diff --git a/database.go b/database.go index fc98bb1..d224c1b 100644 --- a/database.go +++ b/database.go @@ -76,7 +76,7 @@ func db_get_new_card(one_time_code string) (*card, error) { sqlStatement := `SELECT k0_auth_key, k2_cmac_key, k3, k4, card_name` + ` FROM cards WHERE one_time_code=$1 AND` + - ` one_time_code_expiry > NOW() AND one_time_code_used = 'N';` + ` one_time_code_expiry > NOW() AND one_time_code_used = 'N' AND wiped = 'N';` row := db.QueryRow(sqlStatement, one_time_code) err = row.Scan( &c.k0_auth_key, diff --git a/new_card_request.go b/new_card_request.go index 8b71fcd..8f1a4e4 100644 --- a/new_card_request.go +++ b/new_card_request.go @@ -58,7 +58,7 @@ func new_card_request(w http.ResponseWriter, req *http.Request) { if err == sql.ErrNoRows { log.Debug(err) - write_error_message(w, "one time code was used or does not exist") + write_error_message(w, "one time code was used or card was wiped or card does not exist") return } diff --git a/wipeboltcard/database.go b/wipeboltcard/database.go new file mode 100644 index 0000000..9848882 --- /dev/null +++ b/wipeboltcard/database.go @@ -0,0 +1,97 @@ +package main + +import ( + "database/sql" + "errors" + "fmt" + _ "github.com/lib/pq" + "os" +) + +func db_open() (*sql.DB, error) { + + // get connection string from environment variables + + conn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", + os.Getenv("DB_HOST"), + os.Getenv("DB_PORT"), + os.Getenv("DB_USER"), + os.Getenv("DB_PASSWORD"), + os.Getenv("DB_NAME")) + + db, err := sql.Open("postgres", conn) + if err != nil { + return db, err + } + + return db, nil +} + +func db_get_card_name_count(card_name string) (card_count int, err error) { + + card_count = 0 + + db, err := db_open() + if err != nil { + return 0, err + } + defer db.Close() + + sqlStatement := `SELECT COUNT(card_id) FROM cards WHERE card_name = $1;` + + row := db.QueryRow(sqlStatement, card_name) + err = row.Scan(&card_count) + if err != nil { + return 0, err + } + + return card_count, nil +} + +func db_wipe_card(card_name string) (*card_wipe_info, error) { + + card_wipe_info := card_wipe_info{} + + db, err := db_open() + if err != nil { + return &card_wipe_info, err + } + defer db.Close() + + // set card as wiped and disabled + + sqlStatement := `UPDATE cards SET` + + ` lnurlw_enable = 'N', lnurlp_enable = 'N', email_enable = 'N', wiped = 'Y'` + + ` WHERE card_name = $1;` + res, err := db.Exec(sqlStatement, card_name) + if err != nil { + return &card_wipe_info, err + } + count, err := res.RowsAffected() + if err != nil { + return &card_wipe_info, err + } + if count != 1 { + return &card_wipe_info, errors.New("not one card record updated") + } + + // get card keys + + sqlStatement = `SELECT card_id, uid, k0_auth_key, k2_cmac_key, k3, k4` + + ` FROM cards WHERE card_name = $1;` + row := db.QueryRow(sqlStatement, card_name) + err = row.Scan( + &card_wipe_info.id, + &card_wipe_info.uid, + &card_wipe_info.k0, + &card_wipe_info.k2, + &card_wipe_info.k3, + &card_wipe_info.k4) + if err != nil { + return &card_wipe_info, err + } + + card_wipe_info.k1 = os.Getenv("AES_DECRYPT_KEY") + + return &card_wipe_info, nil +} diff --git a/wipeboltcard/main.go b/wipeboltcard/main.go new file mode 100644 index 0000000..69c6aaa --- /dev/null +++ b/wipeboltcard/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "flag" + "fmt" + "strconv" + log "github.com/sirupsen/logrus" + qrcode "github.com/skip2/go-qrcode" +) + +type card_wipe_info struct { + id int + k0 string + k1 string + k2 string + k3 string + k4 string + uid string +} + +func main() { + + card_name_ptr := flag.String("name", "", "select the card to be wiped by name") + + flag.Parse() + + if *card_name_ptr == "" { + flag.PrintDefaults() + return + } + + + // check if card_name exists + + card_count, err := db_get_card_name_count(*card_name_ptr) + if err != nil { + log.Warn(err.Error()) + return + } + + if card_count == 0 { + fmt.Println("the card name does not exist in the database") + return + } + + // set the card as wiped and disabled, get the keys + + card_wipe_info_values, err := db_wipe_card(*card_name_ptr) + if err != nil { + log.Warn(err.Error()) + return + } + + // show a QR code on the console + + qr := `{` + + `"action": "wipe",` + + `"id": ` + strconv.Itoa(card_wipe_info_values.id) + `,` + + `"k0": "` + card_wipe_info_values.k0 + `",` + + `"k1": "` + card_wipe_info_values.k1 + `",` + + `"k2": "` + card_wipe_info_values.k2 + `",` + + `"k3": "` + card_wipe_info_values.k3 + `",` + + `"k4": "` + card_wipe_info_values.k4 + `",` + + `"uid": "` + card_wipe_info_values.uid + `",` + + `"version": 1` + + `}`; + + fmt.Println() + q, err := qrcode.New(qr, qrcode.Medium) + fmt.Println(q.ToSmallString(false)) +} diff --git a/wipeboltcard/wipeboltcard b/wipeboltcard/wipeboltcard new file mode 100755 index 0000000..a055fe2 Binary files /dev/null and b/wipeboltcard/wipeboltcard differ