-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc_test.go
100 lines (87 loc) · 4.41 KB
/
doc_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package crypto
import (
"bytes"
"log"
"time"
)
func Example() {
// Temporary Exposure Keys roll at a frequent cadence called `EKRollingPeriod`,
// which is set to 144, achieving a key validity of 24 hours. Each key is
// randomly and independently generated using a cryptographic random number
// generator. All devices sharing the same `EKRollingPeriod` roll keys at the
// same time — at the beginning of an interval whose ENIntervalNumber is a
// multiple of `EKRollingPeriod`.
log.Println("Creating `TemporaryExposureKey` ...")
tek := NewTemporaryExposureKey()
rollingStartNumber := NewRollingStartNumber(time.Now())
log.Printf("✅ Generated `TemporaryExposureKey` and `rollingStartNumber`: [% #x], %v",
tek, rollingStartNumber)
// `RollingProximityIdentifierKey` is derived from `TemporaryExposureKey`.
// This RPIK should thus also rolled over at the beginning of an interval
// whose ENIntervalNumber is a multiple of `EKRollingPeriod`.
log.Println("Creating `RollingProximityIdentifierKey` ...")
rpik := NewRollingProximityIdentifierKey(tek)
log.Printf("✅ Generated `RollingProximityIdentifierKey`: [% #x]", rpik)
// `AssociatedEncryptedMetadataKey` is derived from `TemporaryExposureKey`.
// This AEMK should thus also rolled over at the beginning of an interval
// whose `ENIntervalNumber` is a multiple of `EKRollingPeriod`.
log.Println("Creating `AssociatedEncryptedMetadataKey` ...")
aemk := NewAssociatedEncryptedMetadataKey(tek)
log.Printf("✅ Generated `AssociatedEncryptedMetadataKey`: [% #x]", aemk)
// Each time the Bluetooth Low Energy MAC randomized address changes, we
// should derive a new `Rolling Proximity Identifier`.
// This privacy-preserving identifier can be broadcast in Bluetooth payloads.
// For example: an RPI to be broadcast 42 minutes after TEK was created.
enin := NewENIntervalNumber(time.Now().Add(42 * time.Minute))
log.Println("Creating `RollingProximityIdentifier` ...")
rpi := NewRollingProximityIdentifier(rpik, enin)
log.Printf("✅ Generated `RollingProximityIdentifier`: [% #x]", rpi)
// For every broadcast, `AssociatedEncryptedMetadata` is generated.
// This data can only be decrypted later if the the user broadcasting it tested
// positive and revealed (uploaded) their `TemporaryExposure`.
log.Println("Creating `AssociatedEncryptedMetadata` ...")
aem := XORKeyStreamAssociatedMetadata(aemk, rpi, []byte("bluetooth metadata..."))
log.Printf("✅ Generated `AssociatedEncryptedMetadata`: [% #x]\n", aem)
// In case of positive diagnosis, a set of TEK and rollingStartNumber ("Diagnosis Key") pairs
// are sent to a central server ...
// Other users periodically download the Diagnosis Key set and match against
// a local repository of previously received RPIs, by subsequently deriving
// the RPK and RPI by running the same hash functions. Let's say for instance
// another user received the TEK we created above, and they previously received
// the RPI we broadcast.
type diagnosisKey struct {
tek TemporaryExposureKey
rollingStartNumber ENIntervalNumber
}
downloadedDiagKeys := []diagnosisKey{
{tek: tek, rollingStartNumber: rollingStartNumber}, // Should match
{tek: tek, rollingStartNumber: rollingStartNumber - EKRollingPeriod*10*3}, // Should NOT match, TEK from 3 days ago
{tek: NewTemporaryExposureKey(), rollingStartNumber: rollingStartNumber}, // Should NOT match, different TEK
}
// Example: A device scanned a RPI and AEM over Bluetooth LE.
receivedRPI := rpi
receivedAEM := aem
for _, diagKey := range downloadedDiagKeys {
receivedTEK := diagKey.tek
derivedRPIK := NewRollingProximityIdentifierKey(receivedTEK)
derivedAEMK := NewAssociatedEncryptedMetadataKey(receivedTEK)
var match bool
for i := 0; i < EKRollingPeriod; i++ {
enin := diagKey.rollingStartNumber + ENIntervalNumber(i)
derivedRPI := NewRollingProximityIdentifier(derivedRPIK, enin)
if bytes.Equal(derivedRPI[:], receivedRPI[:]) {
// There's a match!
// With the derived AEM key, we can now decrypt the AEM that was
// previously received over Bluetooth.
metadata := XORKeyStreamAssociatedMetadata(derivedAEMK, derivedRPI, receivedAEM)
log.Printf("🎉 MATCH: [% #x], interval since `rollingStartNumber` (%v), metadata: [%s]\n", derivedRPI, i, metadata)
match = true
break
}
}
if !match {
log.Printf("❌ No match: [% #x], rollingStartNumber: %v\n",
diagKey.tek, diagKey.rollingStartNumber)
}
}
}