pof

Proof of Freshness: collate proof of an authorship date.

Log | Files | << Repositories


tree f61dd7c61ffad7c4d6472085cd89612ce4d043f1
author esote <esote.net@gmail.com> 1563077126 -0500
committer esote <esote.net@gmail.com> 1563077126 -0500
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQTXAxYDuIzimYoNSPuhTmRAjzzC8gUCXSqqGAAKCRChTmRAjzzC
 8ukVAP4yTXKEQAvVc2Ez7xze1b678C+2TqXvrJahrXb4HxIjQgD+KscvGbHSktC2
 3wTuGYoAxoWmFf8Y3UtPvViAo4R9Ggk=
 =Axf8
 -----END PGP SIGNATURE-----

Proof of Freshness

 README |  80 ++++++++++++++++++++++
 pof.go | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 313 insertions(+)

diff --git a/README b/README
new file mode 100644
index 0000000..8f8bae2
--- /dev/null
+++ b/README
@@ -0,0 +1,80 @@
+Proof of Freshness. Run with:
+
+	$ go get github.com/esote/pof
+	$ cd $GOPATH/src/github.com/esote/pof && go run pof.go
+
+Collate values from multiple sources. Used to prove something cannot have been
+prepared ahead of the date specified within an hour or so.
+
+Current sources:
+	- News (selects top 5 articles)
+		- Der Spiegel International
+		- New York Times World News
+		- BBC World News
+		- Reuters World News
+		- The Economist International
+	- NIST randomness beacon (v1 and v2)
+	- Bitcoin block hash, depth 10
+	- Monero block hash, depth 10
+
+Inspired by the proof of freshness used in Qubes OS's canaries.
+
+Example output:
+
+	Date: 2019-07-14 04:01 UTC
+
+	Src: SPIEGEL ONLINE - International (https://www.spiegel.de/international/index.rss)
+	---
+	Far-Right AfD: Germany's Populist Party Embraces Its Extremist Wing
+	A Question of Trust: Ursula Von Der Leyen Recruits Team to Win Over Brussels
+	Feeding People With Science: Plant Researchers Brace for Population Explosion
+	A Heroine and a Figure of Hate: Carola Rackete and Europe's Troubling Refugee Policies
+	Sea-Watch Captain Carola Rackete: 'We Were All in a State of Total Despair'
+
+	Src: NYT > World News (https://rss.nytimes.com/services/xml/rss/nyt/World.xml)
+	---
+	After a Police Shooting, Ethiopian Israelis Seek a ‘Black Lives Matter’ Reckoning
+	The Taliban Promise to Protect Women. Here’s Why Women Don’t Believe Them.
+	Hong Kong Protest Against Mainland Chinese Traders Turns Violent
+	At Least 26 Dead in Somalia Hotel Attack Claimed by Shabab
+	A ‘Dark Lord’ Falls in Russia’s Growing Power Struggle
+
+	Src: BBC News - World (https://feeds.bbci.co.uk/news/world/rss.xml)
+	---
+	New leak claims Trump scrapped Iran nuclear deal 'to spite Obama'
+	New York power cut: Supply restored in Manhattan
+	Man dies after attacking Tacoma immigration jail, police say
+	Iranian tanker will be released if oil isn't going to Syria, says Hunt
+	France to create new space defence command in September
+
+	Src: Reuters: World News (http://feeds.reuters.com/reuters/worldnews)
+	---
+	Taiwan president travels to Haiti to bolster relations in the region
+	Hong Kong protesters, police clash as demonstrations target Chinese traders
+	Cuba takes first step in railways upgrade with Chinese, Russian help
+	Cuba hopes for slight growth as Trump pummels Caribbean island
+	Monsoon rains kill 30 people in Nepal, others missing
+
+	Src: International (https://www.economist.com/international/rss.xml)
+	---
+	Why are happy people voting for angry parties?
+	Children make parents happy, after all
+	A brief history—and future—of credit scores
+	The Gulf of Guinea is now the world’s worst piracy hotspot
+	Pet-ownership is booming across the world
+
+	Src: NIST Beacon v1 (https://beacon.nist.gov/rest/record/last)
+	---
+	1A13A30ED048D48603F062993C69648C0FBF56DB5FE13FA3817511FFCD6AD1E7C81C7F82055DB20E18993678E9C71BBC1C4228C29C8320CE477DF1CE9945AF1E
+
+	Src: NIST Beacon v2 (https://beacon.nist.gov/beacon/2.0/pulse/last)
+	---
+	541C24E069D78E9728990DF6A490BF3E3B271B9EFFCF5F08851D791048ED5423707F9C5E592C1B75B5ACC23562CECF0F25F14063D0A2831EBC72BD4972355445
+
+	Src: Blockchain.Info [block depth 10] (https://blockchain.info/blocks/?format=json)
+	---
+	0000000000000000000db40b8598bd32f992461dce4c1b8c344b3b140bc5a79f
+
+	Src: Moneroblocks.Info [block depth 10] (https://moneroblocks.info/api/get_block_header/1878020)
+	---
+	cafbc5e7efb2277a6aa90eff6ba3a32b36ac768221b2600cc6b7a41b32645b9a
diff --git a/pof.go b/pof.go
new file mode 100644
index 0000000..67acb3d
--- /dev/null
+++ b/pof.go
@@ -0,0 +1,233 @@
+package main
+
+import (
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"time"
+
+	"github.com/mmcdole/gofeed"
+)
+
+func main() {
+	parser := gofeed.NewParser()
+
+	// date
+	fmt.Printf("Date: %s\n\n",
+		time.Now().UTC().Format("2006-01-02 15:04 MST"))
+
+	// news feeds
+	news(parser, 5)
+
+	// NIST randomness beacons
+	nist()
+
+	// BTC block hash
+	btc()
+
+	// Monero block hash
+	monero()
+}
+
+func news(parser *gofeed.Parser, count int) {
+	urls := []string{
+		"https://www.spiegel.de/international/index.rss",
+		"https://rss.nytimes.com/services/xml/rss/nyt/World.xml",
+		"https://feeds.bbci.co.uk/news/world/rss.xml",
+		"http://feeds.reuters.com/reuters/worldnews",
+		"https://www.economist.com/international/rss.xml",
+	}
+
+	for _, url := range urls {
+		feed, err := parser.ParseURL(url)
+
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		if len(feed.Items) < count {
+			log.Fatalf("couldn't find %d items", count)
+		}
+
+		fmt.Printf("Src: %s (%s)\n---\n", feed.Title, url)
+
+		for i := 0; i < count; i++ {
+			fmt.Printf("%s\n", feed.Items[i].Title)
+		}
+
+		fmt.Println()
+	}
+}
+
+func nist() {
+	v1URL := "https://beacon.nist.gov/rest/record/last"
+	v2URL := "https://beacon.nist.gov/beacon/2.0/pulse/last"
+
+	resp, err := http.Get(v1URL)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	v1XML, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := resp.Body.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	var v1 struct {
+		XMLName     xml.Name `xml:"record"`
+		OutputValue string   `xml:"outputValue"`
+	}
+
+	if err := xml.Unmarshal(v1XML, &v1); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("Src: NIST Beacon v1 (%s)\n---\n", v1URL)
+	fmt.Printf("%s\n\n", v1.OutputValue)
+
+	resp, err = http.Get(v2URL)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	v2JSON, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := resp.Body.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	var v2 struct {
+		Pulse struct {
+			OutputValue string
+		}
+	}
+
+	if err := json.Unmarshal(v2JSON, &v2); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("Src: NIST Beacon v2 (%s)\n---\n", v2URL)
+	fmt.Printf("%s\n\n", v2.Pulse.OutputValue)
+
+}
+
+func btc() {
+	btcURL := "https://blockchain.info/blocks/?format=json"
+
+	resp, err := http.Get(btcURL)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	btcJSON, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := resp.Body.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	var btc struct {
+		Blocks []struct {
+			Hash string
+		}
+	}
+
+	if err := json.Unmarshal(btcJSON, &btc); err != nil {
+		log.Fatal(err)
+	}
+
+	depth := 10
+
+	if len(btc.Blocks) < depth {
+		log.Fatalf("len(btc.Blocks) < %d", depth)
+	}
+
+	fmt.Printf("Src: Blockchain.Info [block depth %d] (%s)\n---\n",
+		depth, btcURL)
+	fmt.Printf("%s\n\n", btc.Blocks[10].Hash)
+}
+
+func monero() {
+	monStatURL := "https://moneroblocks.info/api/get_stats"
+	monURL := "https://moneroblocks.info/api/get_block_header/%d"
+
+	resp, err := http.Get(monStatURL)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	monJSON, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := resp.Body.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	var monStats struct {
+		Height int64
+	}
+
+	if err := json.Unmarshal(monJSON, &monStats); err != nil {
+		log.Fatal(err)
+	}
+
+	depth := int64(10)
+
+	if monStats.Height < depth {
+		log.Fatalf("monStats.Height < %d", depth)
+	}
+
+	resp, err = http.Get(fmt.Sprintf(monURL, monStats.Height-depth))
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	monJSON, err = ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := resp.Body.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	var monBlockHdr struct {
+		BlockHeader struct {
+			Hash string
+		} `json:"block_header"`
+	}
+
+	if err := json.Unmarshal(monJSON, &monBlockHdr); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("Src: Moneroblocks.Info [block depth %d] (%s)\n---\n",
+		depth,
+		fmt.Sprintf(monURL, monStats.Height-depth))
+	fmt.Printf("%s\n\n", monBlockHdr.BlockHeader.Hash)
+
+}