Documentation ¶
Overview ¶
Package crypto is a Go implementation of the crypto functions of Apple/Google’s Exposure Notification framework.
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) } }
Output:
Index ¶
- Constants
- func XORKeyStreamAssociatedMetadata(aemk AssociatedEncryptedMetadataKey, rpi RollingProximityIdentifier, ...) []byte
- type AssociatedEncryptedMetadata
- type AssociatedEncryptedMetadataKey
- type ENIntervalNumber
- type RollingProximityIdentifier
- type RollingProximityIdentifierKey
- type TemporaryExposureKey
Examples ¶
Constants ¶
const EKRollingPeriod = 144
EKRollingPeriod is the duration for which a TemporaryExposureKey is valid (in multiples of 10 minutes). In the protocol, EKRollingPeriod is defined as 144, achieving a key validity of 24 hours.
Variables ¶
This section is empty.
Functions ¶
func XORKeyStreamAssociatedMetadata ¶
func XORKeyStreamAssociatedMetadata( aemk AssociatedEncryptedMetadataKey, rpi RollingProximityIdentifier, data []byte, ) []byte
XORKeyStreamAssociatedMetadata is used to encrypt or decrypt metadata.
Types ¶
type AssociatedEncryptedMetadata ¶
type AssociatedEncryptedMetadata [16]byte
AssociatedEncryptedMetadata is data encrypted along with the RollingProximityIdentifier, and can only be decrypted later if the user broadcasting it tested positive and reveals their TemporaryExposure Key.
type AssociatedEncryptedMetadataKey ¶
type AssociatedEncryptedMetadataKey [16]byte
AssociatedEncryptedMetadataKey is derived from a TemporaryExposureKey in order to encrypt additional metadata.
func NewAssociatedEncryptedMetadataKey ¶
func NewAssociatedEncryptedMetadataKey(tek TemporaryExposureKey) AssociatedEncryptedMetadataKey
NewAssociatedEncryptedMetadataKey returns a new AssociatedEncryptedMetadataKey. It uses HKDF to derive a key from the given TemporaryExposureKey.
type ENIntervalNumber ¶
type ENIntervalNumber uint32
ENIntervalNumber is a number for each 10 minute time window that’s shared between all devices participating in the protocol. These time windows are derived from timestamps in Unix Epoch Time.
func NewENIntervalNumber ¶
func NewENIntervalNumber(t time.Time) ENIntervalNumber
NewENIntervalNumber returns the `ENIntervalNumber`, e.g. the 10 minute time window since Unix Epoch Time, that the given time `t` is in.
func NewRollingStartNumber ¶
func NewRollingStartNumber(t time.Time) ENIntervalNumber
NewRollingStartNumber returns the `ENIntervalNumber` for the start period of a TemporaryExposureKey with generation time `t`.
type RollingProximityIdentifier ¶
type RollingProximityIdentifier [16]byte
RollingProximityIdentifier is a privacy-preserving identifier that is broadcast in Bluetooth payloads.
func NewRollingProximityIdentifier ¶
func NewRollingProximityIdentifier(rpik RollingProximityIdentifierKey, enin ENIntervalNumber) (rpi RollingProximityIdentifier)
NewRollingProximityIdentifier returns a new RollingProximityIdentifier.
type RollingProximityIdentifierKey ¶
type RollingProximityIdentifierKey [16]byte
RollingProximityIdentifierKey (RPIK) is derived from a TemporaryExposureKey and is used in order to derive RollingProximityIdentifier values.
func NewRollingProximityIdentifierKey ¶
func NewRollingProximityIdentifierKey(tek TemporaryExposureKey) RollingProximityIdentifierKey
NewRollingProximityIdentifierKey returns a new RollingProximityIdentifierKey. It uses HKDF to derive a key from the given TemporaryExposureKey.
type TemporaryExposureKey ¶
type TemporaryExposureKey [16]byte
TemporaryExposureKey is a key generated using a cryptographic random number generator. All devices generate a new TEK at the same time — at the beginning of an interval whose ENIntervalNumber is a multiple of EKRollingPeriod.
func NewTemporaryExposureKey ¶
func NewTemporaryExposureKey() (tek TemporaryExposureKey)
NewTemporaryExposureKey returns a new TemporaryExposureKey using `crypto/rand`.