github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/apps/roughtime/agl_roughtime/protocol/protocol.go (about)

     1  // Copyright 2016 The Roughtime Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License. */
    14  
    15  // Package protocol implements the core of the Roughtime protocol.
    16  package protocol
    17  
    18  import (
    19  	"bytes"
    20  	"crypto/sha512"
    21  	"encoding/binary"
    22  	"errors"
    23  	"io"
    24  	"math"
    25  	"sort"
    26  
    27  	"golang.org/x/crypto/ed25519"
    28  )
    29  
    30  const (
    31  	// NonceSize is the number of bytes in a nonce.
    32  	NonceSize = sha512.Size
    33  	// MinRequestSize is the minimum number of bytes in a request.
    34  	MinRequestSize = 1024
    35  
    36  	certificateContext    = "RoughTime v1 delegation signature--\x00"
    37  	signedResponseContext = "RoughTime v1 response signature\x00"
    38  )
    39  
    40  // makeTag converts a four character string into a Roughtime tag value.
    41  func makeTag(tag string) uint32 {
    42  	if len(tag) != 4 {
    43  		panic("makeTag: len(tag) != 4: " + tag)
    44  	}
    45  
    46  	return uint32(tag[0]) | uint32(tag[1])<<8 | uint32(tag[2])<<16 | uint32(tag[3])<<24
    47  }
    48  
    49  var (
    50  	// Various tags used in the Roughtime protocol.
    51  	tagCERT = makeTag("CERT")
    52  	tagDELE = makeTag("DELE")
    53  	tagINDX = makeTag("INDX")
    54  	tagMAXT = makeTag("MAXT")
    55  	tagMIDP = makeTag("MIDP")
    56  	tagMINT = makeTag("MINT")
    57  	tagNONC = makeTag("NONC")
    58  	tagPAD  = makeTag("PAD\xff")
    59  	tagPATH = makeTag("PATH")
    60  	tagPUBK = makeTag("PUBK")
    61  	tagRADI = makeTag("RADI")
    62  	tagROOT = makeTag("ROOT")
    63  	tagSIG  = makeTag("SIG\x00")
    64  	tagSREP = makeTag("SREP")
    65  
    66  	// TagNonce names the bytestring containing the client's nonce.
    67  	TagNonce = tagNONC
    68  )
    69  
    70  // tagsSlice is the type of an array of tags. It provides utility functions so
    71  // that they can be sorted.
    72  type tagsSlice []uint32
    73  
    74  func (t tagsSlice) Len() int           { return len(t) }
    75  func (t tagsSlice) Less(i, j int) bool { return t[i] < t[j] }
    76  func (t tagsSlice) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
    77  
    78  // Encode converts a map of tags to bytestrings into an encoded message. The
    79  // number of elements in msg and the sum of the lengths of all the bytestrings
    80  // must be ≤ 2**32.
    81  func Encode(msg map[uint32][]byte) ([]byte, error) {
    82  	if len(msg) == 0 {
    83  		return make([]byte, 4), nil
    84  	}
    85  
    86  	if len(msg) >= math.MaxInt32 {
    87  		return nil, errors.New("encode: too many tags")
    88  	}
    89  
    90  	var payloadSum uint64
    91  	for _, payload := range msg {
    92  		if len(payload) % 4 != 0 {
    93  			return nil, errors.New("encode: length of value is not a multiple of four")
    94  		}
    95  		payloadSum += uint64(len(payload))
    96  	}
    97  	if payloadSum >= 1<<32 {
    98  		return nil, errors.New("encode: payloads too large")
    99  	}
   100  
   101  	tags := tagsSlice(make([]uint32, 0, len(msg)))
   102  	for tag := range msg {
   103  		tags = append(tags, tag)
   104  	}
   105  	sort.Sort(tags)
   106  
   107  	numTags := uint64(len(tags))
   108  
   109  	encoded := make([]byte, 4*(1+numTags-1+numTags)+payloadSum)
   110  	binary.LittleEndian.PutUint32(encoded, uint32(len(tags)))
   111  	offsets := encoded[4:]
   112  	tagBytes := encoded[4*(1+(numTags-1)):]
   113  	payloads := encoded[4*(1+(numTags-1)+numTags):]
   114  
   115  	currentOffset := uint32(0)
   116  
   117  	for i, tag := range tags {
   118  		payload := msg[tag]
   119  		if i > 0 {
   120  			binary.LittleEndian.PutUint32(offsets, currentOffset)
   121  			offsets = offsets[4:]
   122  		}
   123  
   124  		binary.LittleEndian.PutUint32(tagBytes, tag)
   125  		tagBytes = tagBytes[4:]
   126  
   127  		if len(payload) > 0 {
   128  			copy(payloads, payload)
   129  			payloads = payloads[len(payload):]
   130  			currentOffset += uint32(len(payload))
   131  		}
   132  	}
   133  
   134  	return encoded, nil
   135  }
   136  
   137  // Decode parses the output of encode back into a map of tags to bytestrings.
   138  func Decode(bytes []byte) (map[uint32][]byte, error) {
   139  	if len(bytes) < 4 {
   140  		return nil, errors.New("decode: message too short to be valid")
   141  	}
   142  	if len(bytes) % 4 != 0 {
   143  		return nil, errors.New("decode: message is not a multiple of four bytes")
   144  	}
   145  
   146  	numTags := uint64(binary.LittleEndian.Uint32(bytes))
   147  
   148  	if numTags == 0 {
   149  		return make(map[uint32][]byte), nil
   150  	}
   151  
   152  	if uint64(len(bytes)) < 4*((numTags-1)+numTags) {
   153  		return nil, errors.New("decode: message too short to be valid")
   154  	}
   155  
   156  	offsets := bytes[4:]
   157  	tags := bytes[4*(1+numTags-1):]
   158  	payloads := bytes[4*(1+(numTags-1)+numTags):]
   159  
   160  	if len(payloads) > math.MaxInt32 {
   161  		return nil, errors.New("decode: message too large")
   162  	}
   163  	payloadLength := uint32(len(payloads))
   164  
   165  	currentOffset := uint32(0)
   166  	var lastTag uint32
   167  	ret := make(map[uint32][]byte)
   168  
   169  	for i := uint64(0); i < numTags; i++ {
   170  		tag := binary.LittleEndian.Uint32(tags)
   171  		tags = tags[4:]
   172  
   173  		if i > 0 && lastTag >= tag {
   174  			return nil, errors.New("decode: tags out of order")
   175  		}
   176  
   177  		var nextOffset uint32
   178  		if i < numTags-1 {
   179  			nextOffset = binary.LittleEndian.Uint32(offsets)
   180  			offsets = offsets[4:]
   181  		} else {
   182  			nextOffset = payloadLength
   183  		}
   184  
   185  		if nextOffset%4 != 0 {
   186  			return nil, errors.New("decode: payload length is not a multiple of four bytes")
   187  		}
   188  
   189  		if nextOffset < currentOffset {
   190  			return nil, errors.New("decode: offsets out of order")
   191  		}
   192  
   193  		length := nextOffset - currentOffset
   194  		if uint32(len(payloads)) < length {
   195  			return nil, errors.New("decode: message truncated")
   196  		}
   197  
   198  		payload := payloads[:length]
   199  		payloads = payloads[length:]
   200  		ret[tag] = payload
   201  		currentOffset = nextOffset
   202  	}
   203  
   204  	return ret, nil
   205  }
   206  
   207  // messageOverhead returns the number of bytes needed for Encode to encode the
   208  // given number of tags.
   209  func messageOverhead(numTags int) int {
   210  	return 4 * 2 * numTags
   211  }
   212  
   213  // CalculateChainNonce calculates the nonce to be used in the next request in a
   214  // chain given a reply and a blinding factor.
   215  func CalculateChainNonce(prevReply, blind []byte) (nonce [NonceSize]byte) {
   216  	h := sha512.New()
   217  	h.Write(prevReply)
   218  	prevReplyHash := h.Sum(nil)
   219  
   220  	h.Reset()
   221  	h.Write(prevReplyHash)
   222  	h.Write(blind)
   223  	h.Sum(nonce[:0])
   224  
   225  	return nonce
   226  }
   227  
   228  // CreateRequest creates a Roughtime request given an entropy source and the
   229  // contents of a previous reply for chaining. If this request is the first of a
   230  // chain, prevReply can be empty. It returns the nonce (needed to verify the
   231  // reply), the blind (needed to prove correct chaining to an external party)
   232  // and the request itself.
   233  func CreateRequest(rand io.Reader, prevReply []byte) (nonce, blind [NonceSize]byte, request []byte, err error) {
   234  	if _, err := io.ReadFull(rand, blind[:]); err != nil {
   235  		return nonce, blind, nil, err
   236  	}
   237  
   238  	nonce = CalculateChainNonce(prevReply, blind[:])
   239  
   240  	padding := make([]byte, MinRequestSize-messageOverhead(2)-len(nonce))
   241  	msg, err := Encode(map[uint32][]byte{
   242  		tagNONC: nonce[:],
   243  		tagPAD:  padding,
   244  	})
   245  	if err != nil {
   246  		return nonce, blind, nil, err
   247  	}
   248  
   249  	return nonce, blind, msg, nil
   250  }
   251  
   252  // tree represents a Merkle tree of nonces. Each element of values is a layer
   253  // in the tree, with the widest layer first.
   254  type tree struct {
   255  	values [][][NonceSize]byte
   256  }
   257  
   258  var (
   259  	hashLeafTweak = []byte{0}
   260  	hashNodeTweak = []byte{1}
   261  )
   262  
   263  // hashLeaf hashes an nonce to form the leaf of the Merkle tree.
   264  func hashLeaf(out *[sha512.Size]byte, in []byte) {
   265  	h := sha512.New()
   266  	h.Write(hashLeafTweak)
   267  	h.Write(in)
   268  	h.Sum(out[:0])
   269  }
   270  
   271  // hashNode hashes two child elements of the Merkle tree to produce an interior
   272  // node.
   273  func hashNode(out *[sha512.Size]byte, left, right []byte) {
   274  	h := sha512.New()
   275  	h.Write(hashNodeTweak)
   276  	h.Write(left)
   277  	h.Write(right)
   278  	h.Sum(out[:0])
   279  }
   280  
   281  // newTree creates a Merkle tree given one or more nonces.
   282  func newTree(nonces [][]byte) *tree {
   283  	if len(nonces) == 0 {
   284  		panic("newTree: passed empty slice")
   285  	}
   286  
   287  	levels := 1
   288  	width := len(nonces)
   289  	for width > 1 {
   290  		width = (width + 1) / 2
   291  		levels++
   292  	}
   293  
   294  	ret := &tree{
   295  		values: make([][][NonceSize]byte, 0, levels),
   296  	}
   297  
   298  	leaves := make([][NonceSize]byte, ((len(nonces)+1)/2)*2)
   299  	for i, nonce := range nonces {
   300  		var leaf [NonceSize]byte
   301  		hashLeaf(&leaf, nonce)
   302  		leaves[i] = leaf
   303  	}
   304  	ret.values = append(ret.values, leaves)
   305  
   306  	for i := 1; i < levels; i++ {
   307  		lastLevel := ret.values[i-1]
   308  		width := len(lastLevel) / 2
   309  		if width%2 == 1 {
   310  			width++
   311  		}
   312  		level := make([][NonceSize]byte, width)
   313  		for j := 0; j < len(lastLevel)/2; j++ {
   314  			hashNode(&level[j], lastLevel[j*2][:], lastLevel[j*2+1][:])
   315  		}
   316  		ret.values = append(ret.values, level)
   317  	}
   318  
   319  	return ret
   320  }
   321  
   322  // Root returns the root value of t.
   323  func (t *tree) Root() *[NonceSize]byte {
   324  	return &t.values[len(t.values)-1][0]
   325  }
   326  
   327  // Levels returns the number of levels in t.
   328  func (t *tree) Levels() int {
   329  	return len(t.values)
   330  }
   331  
   332  // Path returns elements from t needed to prove, given the root, that the leaf
   333  // at the given index is in the tree.
   334  func (t *tree) Path(index int) (path [][]byte) {
   335  	path = make([][]byte, 0, len(t.values))
   336  
   337  	for level := 0; level < len(t.values)-1; level++ {
   338  		if index%2 == 1 {
   339  			path = append(path, t.values[level][index-1][:])
   340  		} else {
   341  			path = append(path, t.values[level][index+1][:])
   342  		}
   343  
   344  		index /= 2
   345  	}
   346  
   347  	return path
   348  }
   349  
   350  // CreateReplies signs, using privateKey, a batch of nonces along with the
   351  // given time and radius in microseconds. It returns one reply for each nonce
   352  // using that signature and includes cert in each.
   353  func CreateReplies(nonces [][]byte, midpoint uint64, radius uint32, cert []byte, privateKey []byte) ([][]byte, error) {
   354  	if len(nonces) == 0 {
   355  		return nil, nil
   356  	}
   357  
   358  	tree := newTree(nonces)
   359  
   360  	var midpointBytes [8]byte
   361  	binary.LittleEndian.PutUint64(midpointBytes[:], midpoint)
   362  	var radiusBytes [4]byte
   363  	binary.LittleEndian.PutUint32(radiusBytes[:], radius)
   364  
   365  	signedReply := map[uint32][]byte{
   366  		tagMIDP: midpointBytes[:],
   367  		tagRADI: radiusBytes[:],
   368  		tagROOT: tree.Root()[:],
   369  	}
   370  	signedReplyBytes, err := Encode(signedReply)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  
   375  	toBeSigned := signedResponseContext + string(signedReplyBytes)
   376  	sig := ed25519.Sign(privateKey, []byte(toBeSigned))
   377  
   378  	reply := map[uint32][]byte{
   379  		tagSREP: signedReplyBytes,
   380  		tagSIG:  sig,
   381  		tagCERT: cert,
   382  	}
   383  
   384  	replies := make([][]byte, 0, len(nonces))
   385  
   386  	for i := range nonces {
   387  		var indexBytes [4]byte
   388  		binary.LittleEndian.PutUint32(indexBytes[:], uint32(i))
   389  		reply[tagINDX] = indexBytes[:]
   390  
   391  		path := tree.Path(i)
   392  		pathBytes := make([]byte, 0, NonceSize*len(path))
   393  		for _, pathStep := range path {
   394  			pathBytes = append(pathBytes, pathStep...)
   395  		}
   396  		reply[tagPATH] = pathBytes
   397  
   398  		replyBytes, err := Encode(reply)
   399  		if err != nil {
   400  			return nil, err
   401  		}
   402  
   403  		replies = append(replies, replyBytes)
   404  	}
   405  
   406  	return replies, nil
   407  }
   408  
   409  // CreateCertificate returns a signed certificate, using rootPrivateKey,
   410  // delegating authority for the given timestamp to publicKey.
   411  func CreateCertificate(minTime, maxTime uint64, publicKey, rootPrivateKey []byte) (certBytes []byte, err error) {
   412  	if maxTime < minTime {
   413  		return nil, errors.New("protocol: maxTime < minTime")
   414  	}
   415  
   416  	var minTimeBytes, maxTimeBytes [8]byte
   417  	binary.LittleEndian.PutUint64(minTimeBytes[:], minTime)
   418  	binary.LittleEndian.PutUint64(maxTimeBytes[:], maxTime)
   419  
   420  	signed := map[uint32][]byte{
   421  		tagPUBK: publicKey,
   422  		tagMINT: minTimeBytes[:],
   423  		tagMAXT: maxTimeBytes[:],
   424  	}
   425  
   426  	signedBytes, err := Encode(signed)
   427  	if err != nil {
   428  		return nil, err
   429  	}
   430  
   431  	toBeSigned := certificateContext + string(signedBytes)
   432  	sig := ed25519.Sign(rootPrivateKey, []byte(toBeSigned))
   433  
   434  	cert := map[uint32][]byte{
   435  		tagSIG:  sig,
   436  		tagDELE: signedBytes,
   437  	}
   438  
   439  	return Encode(cert)
   440  }
   441  
   442  func getValue(msg map[uint32][]byte, tag uint32, name string) (value []byte, err error) {
   443  	value, ok := msg[tag]
   444  	if !ok {
   445  		return nil, errors.New("protocol: missing " + name)
   446  	}
   447  	return value, nil
   448  }
   449  
   450  func getFixedLength(msg map[uint32][]byte, tag uint32, name string, length int) (value []byte, err error) {
   451  	value, err = getValue(msg, tag, name)
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	if len(value) != length {
   456  		return nil, errors.New("protocol: incorrect length for " + name)
   457  	}
   458  	return value, nil
   459  }
   460  
   461  func getUint32(msg map[uint32][]byte, tag uint32, name string) (result uint32, err error) {
   462  	valueBytes, err := getFixedLength(msg, tag, name, 4)
   463  	if err != nil {
   464  		return 0, err
   465  	}
   466  	return binary.LittleEndian.Uint32(valueBytes), nil
   467  }
   468  
   469  func getUint64(msg map[uint32][]byte, tag uint32, name string) (result uint64, err error) {
   470  	valueBytes, err := getFixedLength(msg, tag, name, 8)
   471  	if err != nil {
   472  		return 0, err
   473  	}
   474  	return binary.LittleEndian.Uint64(valueBytes), nil
   475  }
   476  
   477  func getSubmessage(msg map[uint32][]byte, tag uint32, name string) (result map[uint32][]byte, err error) {
   478  	valueBytes, err := getValue(msg, tag, name)
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  
   483  	result, err = Decode(valueBytes)
   484  	if err != nil {
   485  		return nil, errors.New("protocol: failed to parse " + name + ": " + err.Error())
   486  	}
   487  
   488  	return result, nil
   489  }
   490  
   491  // VerifyReply parses the Roughtime reply in replyBytes, authenticates it using
   492  // publicKey and verifies that nonce is included in it. It returns the included
   493  // timestamp and radius.
   494  func VerifyReply(replyBytes, publicKey []byte, nonce [NonceSize]byte) (time uint64, radius uint32, err error) {
   495  	reply, err := Decode(replyBytes)
   496  	if err != nil {
   497  		return 0, 0, errors.New("protocol: failed to parse top-level reply: " + err.Error())
   498  	}
   499  
   500  	cert, err := getSubmessage(reply, tagCERT, "certificate")
   501  	if err != nil {
   502  		return 0, 0, err
   503  	}
   504  
   505  	signatureBytes, err := getFixedLength(cert, tagSIG, "signature", ed25519.SignatureSize)
   506  	if err != nil {
   507  		return 0, 0, err
   508  	}
   509  
   510  	delegationBytes, err := getValue(cert, tagDELE, "delegation")
   511  	if err != nil {
   512  		return 0, 0, err
   513  	}
   514  
   515  	if !ed25519.Verify(publicKey, []byte(certificateContext+string(delegationBytes)), signatureBytes) {
   516  		return 0, 0, errors.New("protocol: invalid delegation signature")
   517  	}
   518  
   519  	delegation, err := Decode(delegationBytes)
   520  	if err != nil {
   521  		return 0, 0, errors.New("protocol: failed to parse delegation: " + err.Error())
   522  	}
   523  
   524  	minTime, err := getUint64(delegation, tagMINT, "minimum time")
   525  	if err != nil {
   526  		return 0, 0, err
   527  	}
   528  
   529  	maxTime, err := getUint64(delegation, tagMAXT, "maximum time")
   530  	if err != nil {
   531  		return 0, 0, err
   532  	}
   533  
   534  	delegatedPublicKey, err := getFixedLength(delegation, tagPUBK, "public key", ed25519.PublicKeySize)
   535  	if err != nil {
   536  		return 0, 0, err
   537  	}
   538  
   539  	responseSigBytes, err := getFixedLength(reply, tagSIG, "signature", ed25519.SignatureSize)
   540  	if err != nil {
   541  		return 0, 0, err
   542  	}
   543  
   544  	signedResponseBytes, ok := reply[tagSREP]
   545  	if !ok {
   546  		return 0, 0, errors.New("protocol: response is missing signed portion")
   547  	}
   548  
   549  	if !ed25519.Verify(delegatedPublicKey, []byte(signedResponseContext+string(signedResponseBytes)), responseSigBytes) {
   550  		return 0, 0, errors.New("protocol: invalid response signature")
   551  	}
   552  
   553  	signedResponse, err := Decode(signedResponseBytes)
   554  	if err != nil {
   555  		return 0, 0, errors.New("protocol: failed to parse signed response: " + err.Error())
   556  	}
   557  
   558  	root, err := getFixedLength(signedResponse, tagROOT, "root", sha512.Size)
   559  	if err != nil {
   560  		return 0, 0, err
   561  	}
   562  
   563  	midpoint, err := getUint64(signedResponse, tagMIDP, "midpoint")
   564  	if err != nil {
   565  		return 0, 0, err
   566  	}
   567  
   568  	radius, err = getUint32(signedResponse, tagRADI, "radius")
   569  	if err != nil {
   570  		return 0, 0, err
   571  	}
   572  
   573  	if maxTime < minTime {
   574  		return 0, 0, errors.New("protocol: invalid delegation range")
   575  	}
   576  
   577  	if midpoint < minTime || maxTime < midpoint {
   578  		return 0, 0, errors.New("protocol: timestamp out of range for delegation")
   579  	}
   580  
   581  	index, err := getUint32(reply, tagINDX, "index")
   582  	if err != nil {
   583  		return 0, 0, err
   584  	}
   585  
   586  	path, err := getValue(reply, tagPATH, "path")
   587  	if err != nil {
   588  		return 0, 0, err
   589  	}
   590  	if len(path)%sha512.Size != 0 {
   591  		return 0, 0, errors.New("protocol: path is not a multiple of the hash size")
   592  	}
   593  
   594  	var hash [sha512.Size]byte
   595  	hashLeaf(&hash, nonce[:])
   596  
   597  	for len(path) > 0 {
   598  		pathElementIsRight := index&1 == 0
   599  		if pathElementIsRight {
   600  			hashNode(&hash, hash[:], path[:sha512.Size])
   601  		} else {
   602  			hashNode(&hash, path[:sha512.Size], hash[:])
   603  		}
   604  
   605  		index >>= 1
   606  		path = path[sha512.Size:]
   607  	}
   608  
   609  	if !bytes.Equal(hash[:], root) {
   610  		return 0, 0, errors.New("protocol: calculated tree root doesn't match signed root")
   611  	}
   612  
   613  	return midpoint, radius, nil
   614  }