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) + +}