roughtime.googlesource.com/roughtime.git@v0.0.0-20201210012726-dd529367052d/go/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  	minLen := 4 * (1 + (numTags - 1) + numTags)
   153  
   154  	if uint64(len(bytes)) < minLen {
   155  		return nil, errors.New("decode: message too short to be valid")
   156  	}
   157  
   158  	offsets := bytes[4:]
   159  	tags := bytes[4*(1+numTags-1):]
   160  	payloads := bytes[minLen:]
   161  
   162  	if len(payloads) > math.MaxInt32 {
   163  		return nil, errors.New("decode: message too large")
   164  	}
   165  	payloadLength := uint32(len(payloads))
   166  
   167  	currentOffset := uint32(0)
   168  	var lastTag uint32
   169  	ret := make(map[uint32][]byte)
   170  
   171  	for i := uint64(0); i < numTags; i++ {
   172  		tag := binary.LittleEndian.Uint32(tags)
   173  		tags = tags[4:]
   174  
   175  		if i > 0 && lastTag >= tag {
   176  			return nil, errors.New("decode: tags out of order")
   177  		}
   178  
   179  		var nextOffset uint32
   180  		if i < numTags-1 {
   181  			nextOffset = binary.LittleEndian.Uint32(offsets)
   182  			offsets = offsets[4:]
   183  		} else {
   184  			nextOffset = payloadLength
   185  		}
   186  
   187  		if nextOffset%4 != 0 {
   188  			return nil, errors.New("decode: payload length is not a multiple of four bytes")
   189  		}
   190  
   191  		if nextOffset < currentOffset {
   192  			return nil, errors.New("decode: offsets out of order")
   193  		}
   194  
   195  		length := nextOffset - currentOffset
   196  		if uint32(len(payloads)) < length {
   197  			return nil, errors.New("decode: message truncated")
   198  		}
   199  
   200  		payload := payloads[:length]
   201  		payloads = payloads[length:]
   202  		ret[tag] = payload
   203  		currentOffset = nextOffset
   204  		lastTag = tag
   205  	}
   206  
   207  	return ret, nil
   208  }
   209  
   210  // messageOverhead returns the number of bytes needed for Encode to encode the
   211  // given number of tags.
   212  func messageOverhead(numTags int) int {
   213  	return 4 * 2 * numTags
   214  }
   215  
   216  // CalculateChainNonce calculates the nonce to be used in the next request in a
   217  // chain given a reply and a blinding factor.
   218  func CalculateChainNonce(prevReply, blind []byte) (nonce [NonceSize]byte) {
   219  	h := sha512.New()
   220  	h.Write(prevReply)
   221  	prevReplyHash := h.Sum(nil)
   222  
   223  	h.Reset()
   224  	h.Write(prevReplyHash)
   225  	h.Write(blind)
   226  	h.Sum(nonce[:0])
   227  
   228  	return nonce
   229  }
   230  
   231  // CreateRequest creates a Roughtime request given an entropy source and the
   232  // contents of a previous reply for chaining. If this request is the first of a
   233  // chain, prevReply can be empty. It returns the nonce (needed to verify the
   234  // reply), the blind (needed to prove correct chaining to an external party)
   235  // and the request itself.
   236  func CreateRequest(rand io.Reader, prevReply []byte) (nonce, blind [NonceSize]byte, request []byte, err error) {
   237  	if _, err := io.ReadFull(rand, blind[:]); err != nil {
   238  		return nonce, blind, nil, err
   239  	}
   240  
   241  	nonce = CalculateChainNonce(prevReply, blind[:])
   242  
   243  	padding := make([]byte, MinRequestSize-messageOverhead(2)-len(nonce))
   244  	msg, err := Encode(map[uint32][]byte{
   245  		tagNONC: nonce[:],
   246  		tagPAD:  padding,
   247  	})
   248  	if err != nil {
   249  		return nonce, blind, nil, err
   250  	}
   251  
   252  	return nonce, blind, msg, nil
   253  }
   254  
   255  // tree represents a Merkle tree of nonces. Each element of values is a layer
   256  // in the tree, with the widest layer first.
   257  type tree struct {
   258  	values [][][NonceSize]byte
   259  }
   260  
   261  var (
   262  	hashLeafTweak = []byte{0}
   263  	hashNodeTweak = []byte{1}
   264  )
   265  
   266  // hashLeaf hashes an nonce to form the leaf of the Merkle tree.
   267  func hashLeaf(out *[sha512.Size]byte, in []byte) {
   268  	h := sha512.New()
   269  	h.Write(hashLeafTweak)
   270  	h.Write(in)
   271  	h.Sum(out[:0])
   272  }
   273  
   274  // hashNode hashes two child elements of the Merkle tree to produce an interior
   275  // node.
   276  func hashNode(out *[sha512.Size]byte, left, right []byte) {
   277  	h := sha512.New()
   278  	h.Write(hashNodeTweak)
   279  	h.Write(left)
   280  	h.Write(right)
   281  	h.Sum(out[:0])
   282  }
   283  
   284  // newTree creates a Merkle tree given one or more nonces.
   285  func newTree(nonces [][]byte) *tree {
   286  	if len(nonces) == 0 {
   287  		panic("newTree: passed empty slice")
   288  	}
   289  
   290  	levels := 1
   291  	width := len(nonces)
   292  	for width > 1 {
   293  		width = (width + 1) / 2
   294  		levels++
   295  	}
   296  
   297  	ret := &tree{
   298  		values: make([][][NonceSize]byte, 0, levels),
   299  	}
   300  
   301  	leaves := make([][NonceSize]byte, ((len(nonces)+1)/2)*2)
   302  	for i, nonce := range nonces {
   303  		var leaf [NonceSize]byte
   304  		hashLeaf(&leaf, nonce)
   305  		leaves[i] = leaf
   306  	}
   307  	// Fill any extra leaves with an existing leaf, to simplify analysis
   308  	// that we are not inadvertently signing other messages.
   309  	for i := len(nonces); i < len(leaves); i++ {
   310  		leaves[i] = leaves[0]
   311  	}
   312  	ret.values = append(ret.values, leaves)
   313  
   314  	for i := 1; i < levels; i++ {
   315  		lastLevel := ret.values[i-1]
   316  		width := len(lastLevel) / 2
   317  		if width%2 == 1 {
   318  			width++
   319  		}
   320  		level := make([][NonceSize]byte, width)
   321  		for j := 0; j < len(lastLevel)/2; j++ {
   322  			hashNode(&level[j], lastLevel[j*2][:], lastLevel[j*2+1][:])
   323  		}
   324  		// Fill the extra node with an existing node, to simplify
   325  		// analysis that we are not inadvertently signing other
   326  		// messages.
   327  		if len(lastLevel)/2 < len(level) {
   328  			level[len(lastLevel)/2] = level[0]
   329  		}
   330  		ret.values = append(ret.values, level)
   331  	}
   332  
   333  	return ret
   334  }
   335  
   336  // Root returns the root value of t.
   337  func (t *tree) Root() *[NonceSize]byte {
   338  	return &t.values[len(t.values)-1][0]
   339  }
   340  
   341  // Levels returns the number of levels in t.
   342  func (t *tree) Levels() int {
   343  	return len(t.values)
   344  }
   345  
   346  // Path returns elements from t needed to prove, given the root, that the leaf
   347  // at the given index is in the tree.
   348  func (t *tree) Path(index int) (path [][]byte) {
   349  	path = make([][]byte, 0, len(t.values))
   350  
   351  	for level := 0; level < len(t.values)-1; level++ {
   352  		if index%2 == 1 {
   353  			path = append(path, t.values[level][index-1][:])
   354  		} else {
   355  			path = append(path, t.values[level][index+1][:])
   356  		}
   357  
   358  		index /= 2
   359  	}
   360  
   361  	return path
   362  }
   363  
   364  // CreateReplies signs, using privateKey, a batch of nonces along with the
   365  // given time and radius in microseconds. It returns one reply for each nonce
   366  // using that signature and includes cert in each.
   367  func CreateReplies(nonces [][]byte, midpoint uint64, radius uint32, cert []byte, privateKey []byte) ([][]byte, error) {
   368  	if len(nonces) == 0 {
   369  		return nil, nil
   370  	}
   371  
   372  	tree := newTree(nonces)
   373  
   374  	var midpointBytes [8]byte
   375  	binary.LittleEndian.PutUint64(midpointBytes[:], midpoint)
   376  	var radiusBytes [4]byte
   377  	binary.LittleEndian.PutUint32(radiusBytes[:], radius)
   378  
   379  	signedReply := map[uint32][]byte{
   380  		tagMIDP: midpointBytes[:],
   381  		tagRADI: radiusBytes[:],
   382  		tagROOT: tree.Root()[:],
   383  	}
   384  	signedReplyBytes, err := Encode(signedReply)
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  
   389  	toBeSigned := signedResponseContext + string(signedReplyBytes)
   390  	sig := ed25519.Sign(privateKey, []byte(toBeSigned))
   391  
   392  	reply := map[uint32][]byte{
   393  		tagSREP: signedReplyBytes,
   394  		tagSIG:  sig,
   395  		tagCERT: cert,
   396  	}
   397  
   398  	replies := make([][]byte, 0, len(nonces))
   399  
   400  	for i := range nonces {
   401  		var indexBytes [4]byte
   402  		binary.LittleEndian.PutUint32(indexBytes[:], uint32(i))
   403  		reply[tagINDX] = indexBytes[:]
   404  
   405  		path := tree.Path(i)
   406  		pathBytes := make([]byte, 0, NonceSize*len(path))
   407  		for _, pathStep := range path {
   408  			pathBytes = append(pathBytes, pathStep...)
   409  		}
   410  		reply[tagPATH] = pathBytes
   411  
   412  		replyBytes, err := Encode(reply)
   413  		if err != nil {
   414  			return nil, err
   415  		}
   416  
   417  		replies = append(replies, replyBytes)
   418  	}
   419  
   420  	return replies, nil
   421  }
   422  
   423  // CreateCertificate returns a signed certificate, using rootPrivateKey,
   424  // delegating authority for the given timestamp to publicKey.
   425  func CreateCertificate(minTime, maxTime uint64, publicKey, rootPrivateKey []byte) (certBytes []byte, err error) {
   426  	if maxTime < minTime {
   427  		return nil, errors.New("protocol: maxTime < minTime")
   428  	}
   429  
   430  	var minTimeBytes, maxTimeBytes [8]byte
   431  	binary.LittleEndian.PutUint64(minTimeBytes[:], minTime)
   432  	binary.LittleEndian.PutUint64(maxTimeBytes[:], maxTime)
   433  
   434  	signed := map[uint32][]byte{
   435  		tagPUBK: publicKey,
   436  		tagMINT: minTimeBytes[:],
   437  		tagMAXT: maxTimeBytes[:],
   438  	}
   439  
   440  	signedBytes, err := Encode(signed)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  
   445  	toBeSigned := certificateContext + string(signedBytes)
   446  	sig := ed25519.Sign(rootPrivateKey, []byte(toBeSigned))
   447  
   448  	cert := map[uint32][]byte{
   449  		tagSIG:  sig,
   450  		tagDELE: signedBytes,
   451  	}
   452  
   453  	return Encode(cert)
   454  }
   455  
   456  func getValue(msg map[uint32][]byte, tag uint32, name string) (value []byte, err error) {
   457  	value, ok := msg[tag]
   458  	if !ok {
   459  		return nil, errors.New("protocol: missing " + name)
   460  	}
   461  	return value, nil
   462  }
   463  
   464  func getFixedLength(msg map[uint32][]byte, tag uint32, name string, length int) (value []byte, err error) {
   465  	value, err = getValue(msg, tag, name)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	if len(value) != length {
   470  		return nil, errors.New("protocol: incorrect length for " + name)
   471  	}
   472  	return value, nil
   473  }
   474  
   475  func getUint32(msg map[uint32][]byte, tag uint32, name string) (result uint32, err error) {
   476  	valueBytes, err := getFixedLength(msg, tag, name, 4)
   477  	if err != nil {
   478  		return 0, err
   479  	}
   480  	return binary.LittleEndian.Uint32(valueBytes), nil
   481  }
   482  
   483  func getUint64(msg map[uint32][]byte, tag uint32, name string) (result uint64, err error) {
   484  	valueBytes, err := getFixedLength(msg, tag, name, 8)
   485  	if err != nil {
   486  		return 0, err
   487  	}
   488  	return binary.LittleEndian.Uint64(valueBytes), nil
   489  }
   490  
   491  func getSubmessage(msg map[uint32][]byte, tag uint32, name string) (result map[uint32][]byte, err error) {
   492  	valueBytes, err := getValue(msg, tag, name)
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  
   497  	result, err = Decode(valueBytes)
   498  	if err != nil {
   499  		return nil, errors.New("protocol: failed to parse " + name + ": " + err.Error())
   500  	}
   501  
   502  	return result, nil
   503  }
   504  
   505  // VerifyReply parses the Roughtime reply in replyBytes, authenticates it using
   506  // publicKey and verifies that nonce is included in it. It returns the included
   507  // timestamp and radius.
   508  func VerifyReply(replyBytes, publicKey []byte, nonce [NonceSize]byte) (time uint64, radius uint32, err error) {
   509  	reply, err := Decode(replyBytes)
   510  	if err != nil {
   511  		return 0, 0, errors.New("protocol: failed to parse top-level reply: " + err.Error())
   512  	}
   513  
   514  	cert, err := getSubmessage(reply, tagCERT, "certificate")
   515  	if err != nil {
   516  		return 0, 0, err
   517  	}
   518  
   519  	signatureBytes, err := getFixedLength(cert, tagSIG, "signature", ed25519.SignatureSize)
   520  	if err != nil {
   521  		return 0, 0, err
   522  	}
   523  
   524  	delegationBytes, err := getValue(cert, tagDELE, "delegation")
   525  	if err != nil {
   526  		return 0, 0, err
   527  	}
   528  
   529  	if !ed25519.Verify(publicKey, []byte(certificateContext+string(delegationBytes)), signatureBytes) {
   530  		return 0, 0, errors.New("protocol: invalid delegation signature")
   531  	}
   532  
   533  	delegation, err := Decode(delegationBytes)
   534  	if err != nil {
   535  		return 0, 0, errors.New("protocol: failed to parse delegation: " + err.Error())
   536  	}
   537  
   538  	minTime, err := getUint64(delegation, tagMINT, "minimum time")
   539  	if err != nil {
   540  		return 0, 0, err
   541  	}
   542  
   543  	maxTime, err := getUint64(delegation, tagMAXT, "maximum time")
   544  	if err != nil {
   545  		return 0, 0, err
   546  	}
   547  
   548  	delegatedPublicKey, err := getFixedLength(delegation, tagPUBK, "public key", ed25519.PublicKeySize)
   549  	if err != nil {
   550  		return 0, 0, err
   551  	}
   552  
   553  	responseSigBytes, err := getFixedLength(reply, tagSIG, "signature", ed25519.SignatureSize)
   554  	if err != nil {
   555  		return 0, 0, err
   556  	}
   557  
   558  	signedResponseBytes, ok := reply[tagSREP]
   559  	if !ok {
   560  		return 0, 0, errors.New("protocol: response is missing signed portion")
   561  	}
   562  
   563  	if !ed25519.Verify(delegatedPublicKey, []byte(signedResponseContext+string(signedResponseBytes)), responseSigBytes) {
   564  		return 0, 0, errors.New("protocol: invalid response signature")
   565  	}
   566  
   567  	signedResponse, err := Decode(signedResponseBytes)
   568  	if err != nil {
   569  		return 0, 0, errors.New("protocol: failed to parse signed response: " + err.Error())
   570  	}
   571  
   572  	root, err := getFixedLength(signedResponse, tagROOT, "root", sha512.Size)
   573  	if err != nil {
   574  		return 0, 0, err
   575  	}
   576  
   577  	midpoint, err := getUint64(signedResponse, tagMIDP, "midpoint")
   578  	if err != nil {
   579  		return 0, 0, err
   580  	}
   581  
   582  	radius, err = getUint32(signedResponse, tagRADI, "radius")
   583  	if err != nil {
   584  		return 0, 0, err
   585  	}
   586  
   587  	if maxTime < minTime {
   588  		return 0, 0, errors.New("protocol: invalid delegation range")
   589  	}
   590  
   591  	if midpoint < minTime || maxTime < midpoint {
   592  		return 0, 0, errors.New("protocol: timestamp out of range for delegation")
   593  	}
   594  
   595  	index, err := getUint32(reply, tagINDX, "index")
   596  	if err != nil {
   597  		return 0, 0, err
   598  	}
   599  
   600  	path, err := getValue(reply, tagPATH, "path")
   601  	if err != nil {
   602  		return 0, 0, err
   603  	}
   604  	if len(path)%sha512.Size != 0 {
   605  		return 0, 0, errors.New("protocol: path is not a multiple of the hash size")
   606  	}
   607  
   608  	var hash [sha512.Size]byte
   609  	hashLeaf(&hash, nonce[:])
   610  
   611  	for len(path) > 0 {
   612  		pathElementIsRight := index&1 == 0
   613  		if pathElementIsRight {
   614  			hashNode(&hash, hash[:], path[:sha512.Size])
   615  		} else {
   616  			hashNode(&hash, path[:sha512.Size], hash[:])
   617  		}
   618  
   619  		index >>= 1
   620  		path = path[sha512.Size:]
   621  	}
   622  
   623  	if !bytes.Equal(hash[:], root) {
   624  		return 0, 0, errors.New("protocol: calculated tree root doesn't match signed root")
   625  	}
   626  
   627  	return midpoint, radius, nil
   628  }