jedi

package module
v0.0.0-...-a2ff7f2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2019 License: BSD-3-Clause Imports: 19 Imported by: 1

README

JEDI Protocol Implementation

This repository implements the JEDI protocol described in our paper:

Sam Kumar, Yuncong Hu, Michael P Andersen, Raluca Ada Popa, and David E. Culler. JEDI: Many-to-Many End-to-End Encryption and Key Delegation for IoT. 28th USENIX Security Symposium. August 2019.

This implementation of JEDI is a library that implements the JEDI protocol for end-to-end encryption. It is written to be mostly independent from the underlying system used to transport messages. It is meant to be used by a messaging system to encrypt/decrypt messages and delegate keys, by making API calls to this JEDI library. We have integrated JEDI into WAVEMQ, a publish-subscribe system that uses WAVE for authorization, in this manner. The WAVEMQ service, responsible for sending/receiving messages, makes API calls to JEDI to encrypt/decrypt messages. WAVE, the authorization sytem used by WAVEMQ, makes API calls to JEDI to include keys in its attestations for delegation. The code for that integration is available in the GitHub repositories for WAVE and WAVEMQ, at https://github.com/immesys/wave and https://github.com/immesys/wavemq, respectively.

Our original JEDI implementation was tied closely to bw2, the predecessor to WAVE. As bw2 fell out of use and was supplanted by WAVE and WAVEMQ, we found it necessary to overhaul our original implementation so that it is not closely tied to the underlying syndication system. If you are interested in our original implementation (for example, to reproduce our results from Section 7.2 of the paper), then please contact us.

JEDI's new WKD-IBE encryption algorithm, assembly-optimized for Cortex-M0+, x86-64, and ARMv8 platforms, is not in this repository. It is available at http://github.com/ucbrise/jedi-pairing. Our implementation of JEDI in this repository uses that code as a black box.

What is JEDI?

JEDI is an end-to-end encryption protocol for decoupled, many-to-many communication. One example of such communication is the publish-subscribe paradigm. In publish-subscribe systems, the sender of a message does not know who will receive the message. The sender labels the message with a topic (sometimes called a resource), and an intermediate broker service is responsible for forwarding the message to those interested in that topic.

In such settings, traditional end-to-end protocols, like SSL/TLS, do not directly apply. These protocols typically require the sender of a message to encrypt it using the recipient's public key, but this is impossible in decoupled, many-to-many communication because the sender doesn't know who the recipients are.

JEDI allows the sender to encrypt messages in such a way that only those who are authorized to receive messages for the relevant topic to decrypt the message. We designed JEDI with IoT-oriented publish-subscribe use cases in mind, so it supports fine-grained expiry, hierarchically-structured topics, and decentralized delegation. The intermediate broker does not participate at all in the JEDI protocol, so it applies generally to decoupled, many-to-many communication (e.g., multicast), not just publish-subscribe.

The acronym JEDI stands for Joining Encryption and Delegation for IoT.

License

The code in this repository is open-source under the BSD 3-Clause License.

Documentation

Index

Constants

View Source
const (
	PatternTypeDecryption = iota
	PatternTypeSigning
)

These constants enumerate the types of WKD-IBE secret keys in the key store.

View Source
const (
	MarshalledTypeInvalid = iota
	MarshalledTypePattern
	MarshalledTypeDelegation
)

These constants define the byte corresponding to each type of marshalled object.

View Source
const (
	MinYear = 2015
	MaxYear = 2050

	MinMonth = 1
	MaxMonth = 12

	MinFiveDays = 1
	MaxFiveDays = 6

	MinDay                 = 1
	MaxDay                 = 31
	MaxDayShortMonth       = 30
	MaxDayFebruary         = 28
	MaxDayFebruaryLeapYear = 29

	MinSixHours = 1
	MaxSixHours = 4

	MinHour = 0
	MaxHour = 23
)

These constants describe the minimum and maximum values of each component in a TimePath.

View Source
const AESKeySize = 16

AESKeySize is the key size to use with AES, in bytes.

View Source
const EndOfURISymbol = '$'

EndOfURISymbol is the sentinel used at the end of non-prefix URIs to prevent further delegation.

View Source
const MarshalledLengthLength = 4

MarshalledLengthLength is the length, when marshalled, of an integer representing the length of a marshalled object.

View Source
const MaxTimeLength = 6

MaxTimeLength is the maximum length of a TimePath.

Variables

EncryptedKeySize the size (in bytes) of the WKD-IBE ciphertext of the symmetric key at the beginning of each JEDI ciphertext.

Functions

func DecodePattern

func DecodePattern(pattern Pattern) (URIPath, TimePath)

DecodePattern decodes a pattern encoded as a byte slice for each component back into its component URI and time.

func EncodePattern

func EncodePattern(uripath URIPath, timepath TimePath, into Pattern)

EncodePattern encodes a URIPath and TimePath into a pattern, where each component is represented as a byte slice. The slice into which to encode the pattern is provided as an argument. This is designed to be a helper function; the "PatternEncoder" interface is designed to support flexible, application-dependent encoding.

func EncodeTimePathInto

func EncodeTimePathInto(tp TimePath, into Pattern)

EncodeTimePathInto encodes a TimePath into a pattern, where each component is represented as a byte slice. The slice into which to encode the pattern is provided as an argument.

func EncodeURIPathInto

func EncodeURIPathInto(up URIPath, into Pattern)

EncodeURIPathInto encodes a URIPath into a pattern, where each component is represented as a byte slice. The slice into which to encode the pattern is provided as an argument.

func TimeComponentBounds

func TimeComponentBounds(prefix TimePath, position TimeComponentPosition) (uint16, uint16)

TimeComponentBounds takes a prefix of a TimePath and the position of an unused component, and returns the minimum and maximum values of that component in the TimePath, restricted by the values of components in the provided prefix.

func TimeToBytes

func TimeToBytes(tp TimePath) []byte

TimeToBytes marshals a TimePath into a string of bytes.

func URIToBytes

func URIToBytes(up URIPath) []byte

URIToBytes marshals a URIPath into a string of bytes.

func ValidateTimeComponent

func ValidateTimeComponent(prefix TimePath, quantity uint16, position TimeComponentPosition) bool

ValidateTimeComponent takes a TimePath prefix, and position and quantity of a proposed component later in the path, and returns a boolean indicating whether the proposed component is valid.

func ValidateURIComponent

func ValidateURIComponent(uri string) bool

ValidateURIComponent takes a string and returns a boolean indicating if it represents a valid URI component.

Types

type ClientState

type ClientState struct {
	// contains filtered or unexported fields
}

ClientState is the state that JEDI principals keep in memory to accelerate encryption, decryption, signing, and verification of messages.

func NewClientState

func NewClientState(public PublicInfoReader, keys KeyStoreReader, encoder PatternEncoder, capacity uint64) *ClientState

NewClientState creates a new ClientState abstraction with the specified abstraction to the key store, algorithm to encode patterns, and memory capacity (in bytes) to cache objects to accelerate JEDI's crypto operations.

func (*ClientState) Decrypt

func (state *ClientState) Decrypt(ctx context.Context, hierarchy []byte, uri string, timestamp time.Time, encrypted []byte) ([]byte, error)

Decrypt decrypts a message encrypted with JEDI, reading from and mutating the ClientState instance on which the function is invoked. It's very important that message's integrity (e.g., signature) is verified before calling this function. If not, an attacker could get us to decrypt a message with the "wrong" URI/time; if this happens, an incorrect symmetric key will be cached in the ClientState, denying service for future proper messages reusing that pattern.

func (*ClientState) DecryptSeparated

func (state *ClientState) DecryptSeparated(ctx context.Context, hierarchy []byte, uri string, timestamp time.Time, encryptedKey []byte, encryptedMessage []byte) ([]byte, error)

DecryptSeparated is the same as Decrypt, but accepts the encrypted message in two parts: the WKD-IBE ciphertext of the encrypted symmetric key, and the symmetric-key ciphertext of the encrypted message.

func (*ClientState) DecryptWithPattern

func (state *ClientState) DecryptWithPattern(ctx context.Context, hierarchy []byte, pattern Pattern, encryptedKey []byte, encryptedMessage []byte) ([]byte, error)

DecryptWithPattern is the same as Decrypt, but requires the Pattern to be already formed. This is useful if the pattern itself was sent with the message and is available directly in lieu of the URI and timestamp.

func (*ClientState) Encrypt

func (state *ClientState) Encrypt(ctx context.Context, hierarchy []byte, uri string, timestamp time.Time, message []byte) ([]byte, error)

Encrypt encrypts a message using JEDI, reading from and mutating the ClientState instance on which the function is invoked. The "timestamp" argument should be set to the current time in most cases, which can be obtained by calling time.Now(). The function will work with any URI/time combination, but for a single URI you should try to move chronologically in time for the best performance.

func (*ClientState) EncryptWithPattern

func (state *ClientState) EncryptWithPattern(ctx context.Context, hierarchy []byte, uriPath URIPath, pattern Pattern, message []byte) ([]byte, error)

EncryptWithPattern is like Encrypt, but requires the Pattern to already be formed. This is useful if you've already parsed the URI, or are working with the URI components directly.

type DefaultPatternEncoder

type DefaultPatternEncoder struct {
	// contains filtered or unexported fields
}

DefaultPatternEncoder is a simple pattern encoding that will likely be suitable for many applications.

func NewDefaultPatternEncoder

func NewDefaultPatternEncoder(maxURILength int) *DefaultPatternEncoder

NewDefaultPatternEncoder creates a new DefaultPatternEncoder, capable of supporting the specified URI length, and returns it.

func (*DefaultPatternEncoder) Encode

func (dpe *DefaultPatternEncoder) Encode(uriPath URIPath, timePath TimePath, patternType PatternType) Pattern

Encode encodes a URI and time into a pattern, using the default encoding. It attaches a prefix to each component of the pattern to distinguish decryption keys from signing keys, but does not introduce any extra components in the pattern.

type Delegation

type Delegation struct {
	Hierarchy []byte
	Params    *wkdibe.Params
	Patterns  []Pattern
	Keys      []*wkdibe.SecretKey
}

Delegation is a bundle of keys that delegate permission.

func Delegate

func Delegate(ctx context.Context, ks KeyStoreReader, pe PatternEncoder, hierarchy []byte, uri string, start time.Time, end time.Time, perm Permission) (*Delegation, error)

Delegate creates a new JEDI delegation conveying some permissions on a URI or URI prefix for a time range.

func DelegateParsed

func DelegateParsed(ctx context.Context, ks KeyStoreReader, pe PatternEncoder, hierarchy []byte, uriPath URIPath, timePaths []TimePath, perm Permission) (*Delegation, error)

DelegateParsed creates a new JEDI delegation conveying permissions on a URI or URI prefix for the set of indicated times.

func DelegatePatterns

func DelegatePatterns(ctx context.Context, ks KeyStoreReader, hierarchy []byte, patterns []Pattern) (*Delegation, error)

DelegatePatterns creates a new JEDI delegation granting the permissions conveyed in the set of provided patterns.

func (*Delegation) Marshal

func (d *Delegation) Marshal() []byte

Marshal encodes a JEDI delegation into a byte array.

func (*Delegation) Unmarshal

func (d *Delegation) Unmarshal(marshalled []byte) bool

Unmarshal decodes a JEDI delegation from a byte array.

type KeyStoreReader

type KeyStoreReader interface {
	// KeyForPattern retrieves a key whose pattern matches the provided
	// pattern, where "matches" is defined as in Section 3.1 of the JEDI paper
	// (see the README.md file for a full citation of the paper). The pattern
	// should be encoded from a URI and time using the application's
	// PatternEncoder.
	KeyForPattern(ctx context.Context, hierarchy []byte, pattern Pattern) (*wkdibe.Params, *wkdibe.SecretKey, error)
}

KeyStoreReader represents a read-only interface to a key store that can be used with JEDI. It represents the interface that a key store must support so that JEDI can properly read from it when encrypting messages, decrypting messages, and creating delegations.

Currently there's no implementation of this interface in this library. All of the applications to which we've applied JEDI so far have their own mechanisms to exchange keys; to apply this library to such systems, one would lift the interface to the application's locally stored keys to this interface. This allows the functions in this library to read the relevant keys from the local storage infrastructure that's already part of the application.

If we apply JEDI to an application that doesn't have this functionality, I would implement a "default" key store, satisfying this interface, that could be used to provide the functionality.

type Marshallable

type Marshallable interface {
	Marshal() []byte
	Unmarshal([]byte) bool
}

Marshallable represents an object that can be marshalled.

type MarshalledType

type MarshalledType byte

MarshalledType is a byte that describes the type of a marshalled object.

func (MarshalledType) Byte

func (marshalledType MarshalledType) Byte() byte

Byte returns a byte representation of a MarshalledType.

type Pattern

type Pattern [][]byte

Pattern describes a pattern encoding a URIPath and TimePath, represented as a list of byte slices.

func (Pattern) Equals

func (p Pattern) Equals(q Pattern) bool

Equals returns a boolean indicating whther this Pattern equals the one provided as an argument.

func (Pattern) GetComponent

func (p Pattern) GetComponent(index int) PatternComponent

GetComponent returns a component of the Pattern, abstracted as a PatternComponent.

func (Pattern) Marshal

func (p Pattern) Marshal() []byte

Marshal encodes a Pattern into a byte slice.

func (Pattern) Matches

func (p Pattern) Matches(q Pattern) bool

Matches returns a boolean indicating whether this Pattern matches the one provided as an argument. The term "matches" is defined in Section 3.1 of the JEDI paper (see the README.md file for a full citation of the paper).

func (Pattern) ToAttrs

func (p Pattern) ToAttrs() wkdibe.AttributeList

ToAttrs converts a pattern to a WKD-IBE attribute list by hashing each component.

func (Pattern) ToAttrsWithReference

func (p Pattern) ToAttrsWithReference(q Pattern, qAttrs wkdibe.AttributeList) (wkdibe.AttributeList, bool)

ToAttrsWithReference is the same as ToAttrs, but it uses a similar pattern and its corresponding WKD-IBE attribute list to avoid hashing where possible. Some of the big integers in the returned attribute list may be aliased with those in the provided attribute list. the returned bool indicates if p and q are equal.

func (*Pattern) Unmarshal

func (p *Pattern) Unmarshal(marshalled []byte) bool

Unmarshal decodes a Pattern from a byte slice encoded with Marshal().

type PatternComponent

type PatternComponent interface {
	Type() PatternComponentType
	String() string

	// These functions are useful if you don't want to use a type assertion
	// but know what the underlying type is.
	Name() string
	Quantity() uint16
}

PatternComponent is the interface satsified by URIComponent and TimeComponent. It describes a component in a pattern.

type PatternComponentType

type PatternComponentType int

PatternComponentType encodes the type of a pattern component.

const (
	URIComponentType PatternComponentType = iota
	TimeComponentType
)

These constants describe the types of pattern components.

type PatternEncoder

type PatternEncoder interface {
	// Encode encodes URI and time into a pattern.
	Encode(uriPath URIPath, timePath TimePath, patternType PatternType) Pattern
}

PatternEncoder represents an algorithm to encode a URI and time into a pattern. The EncodePattern() function is a good starting point, but a real application needs to distinguish between encryption and signing keys, and may choose to use extra slots to distinguish JEDI keys from other uses of WKD-IBE.

type PatternType

type PatternType int

PatternType describes a type of permission encoded by a pattern.

type Permission

type Permission uint32

Permission indicates the type of access that is being granted. It is a bit vector.

const (
	DecryptPermission Permission = 0x1
	SignPermission    Permission = 0x2
)

These constants are the base Permissions that can be combined via bitwise OR.

type PublicInfoReader

type PublicInfoReader interface {
	// ParamsForHierarchy retrieves the WKD-IBE public parameters used for a
	// hierarchy.
	ParamsForHierarchy(ctx context.Context, hierarchy []byte) (*wkdibe.Params, error)
}

PublicInfoReader represents a read-only interface to the public parameters for each hierarchy. It is similar to KeyStoreReader, in that it is meant to be implemented by the calling application.

type TimeComponent

type TimeComponent []byte

TimeComponent describes a component of a URIPath.

func NewTimeComponent

func NewTimeComponent(quantity uint16, position TimeComponentPosition) TimeComponent

NewTimeComponent creates a new TimeComponent with the given quantity and position.

func (TimeComponent) Name

func (tc TimeComponent) Name() string

Name panics.

func (TimeComponent) Position

func (tc TimeComponent) Position() TimeComponentPosition

Position returns the position (which corresponds to the semantics) of this component within a TimePath.

func (TimeComponent) Quantity

func (tc TimeComponent) Quantity() uint16

Quantity returns the quantity associated with this TimeComponent.

func (TimeComponent) String

func (tc TimeComponent) String() string

String returns a printable string representing this TimeComponent.

func (TimeComponent) Type

Type returns the value TimeComponentType.

type TimeComponentPosition

type TimeComponentPosition uint8

TimeComponentPosition describes the location (which defines the semantics) of a TimeComponent in a TimePath.

const (
	TimeComponentPositionYear TimeComponentPosition = iota
	TimeComponentPositionMonth
	TimeComponentPositionFiveDays
	TimeComponentPositionDay
	TimeComponentPositionSixHours
	TimeComponentPositionHour
)

These constants enumerate the valid positions of a TimeComponent (i.e., the valid values of a TimeComponentPosition).

We divide time into components as follows: Year Month (always twelve per year) Five-Day Periods (always six per month, last one may be shorter or longer) Day (always five per five-day period) Six-Hour Periods (always four per day)

For example, 16 Feb 2017 at 5 PM is represented as follows: 2017/02/3/16/2/17 2017 represents year 2017 02 represents February 3 represents five-day period starting on the 16th 16 represents day 16 2 represents six-hour period starting at noon 17 represents hour 17

func (TimeComponentPosition) String

func (ecp TimeComponentPosition) String() string

String returns a human-readable string describing the semantics of the provided TimeComponentPosition.

type TimePath

type TimePath []TimeComponent

TimePath is a hierarchical representation of a point in time, at the granularity supported by JEDI's expiry.

func DecodeTimePathFrom

func DecodeTimePathFrom(from Pattern) TimePath

DecodeTimePathFrom decodes a TimePath from a pattern, where each component is represented as a byte slice.

func ParseTime

func ParseTime(time time.Time) (TimePath, error)

ParseTime takes a time.Time and returns a TimePath representing that time.

func ParseTimeFromPath

func ParseTimeFromPath(timePath []uint16) (TimePath, error)

ParseTimeFromPath takes a slice of time components and produces a TimePath representing that time or time prefix.

func TimeFromBytes

func TimeFromBytes(marshalled []byte) TimePath

TimeFromBytes unmarshals a TimePath from a string of bytes marshalled with IDToBytes.

func TimeRange

func TimeRange(start time.Time, end time.Time) ([]TimePath, error)

TimeRange returns all time paths such that START <= path <= END. It is useful for computing which keys to grant for expiry.

func TimeRangeFromPaths

func TimeRangeFromPaths(startPath TimePath, endPath TimePath) []TimePath

TimeRangeFromPaths is a function that returns all time paths such that STARTPATH <= path <= ENDPATH. Useful for computing which keys to grant for expiry. STARTPATH and ENDPATH are fully-qualified paths.

func (TimePath) String

func (tp TimePath) String() string

String returns a human-readable string representing this TimePath.

type URIComponent

type URIComponent []byte

URIComponent describes a component of a URIPath.

func NewURIComponent

func NewURIComponent(name string, position URIComponentPosition) URIComponent

NewURIComponent creates a new URIComponent with the given name and position.

func (URIComponent) Name

func (uc URIComponent) Name() string

Name returns the name associated with this URIComponent.

func (URIComponent) Position

func (uc URIComponent) Position() URIComponentPosition

Position returns the index of this URIComponent within a URIPath.

func (URIComponent) Quantity

func (uc URIComponent) Quantity() uint16

Quantity panics.

func (URIComponent) String

func (uc URIComponent) String() string

String returns a printable string representing this URIComponent.

func (URIComponent) Type

Type returns the value URIComponentType.

type URIComponentPosition

type URIComponentPosition uint8

URIComponentPosition describes the index of a URIComponent in a URIPath.

type URIPath

type URIPath []URIComponent

URIPath represents a URI or URI prefix.

func DecodeURIPathFrom

func DecodeURIPathFrom(from Pattern) URIPath

DecodeURIPathFrom decodes a URIPath from a pattern, where each component is represented as a byte slice.

func ParseURI

func ParseURI(uri string) (URIPath, error)

ParseURI takes a string representing a URI or URIPrefix and outputs a URIPath representing it.

func ParseURIFromPath

func ParseURIFromPath(uriPath []string) (URIPath, error)

ParseURIFromPath takes a slice of URI components and produces a URIPath representing that URI or URI prefix.

func URIFromBytes

func URIFromBytes(marshalled []byte) URIPath

URIFromBytes unmarshals a URIPath from a string of bytes marshalled with IDToBytes.

func (URIPath) String

func (up URIPath) String() string

String returns a human-readable string representing this URIPath.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL