blob: 72dc2e91ad25bbd5ab1dc64d195d044b07604916 [file] [log] [blame]
// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ee
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"encoding/binary"
"math/big"
"upspin.io/errors"
"upspin.io/pack/packutil"
"upspin.io/upspin"
)
// wrappedKey encodes a key that will decrypt and verify the ciphertext.
type wrappedKey struct {
keyHash []byte // recipient's public key
dkey []byte // ciphertext symmetric decryption key
nonce []byte
ephemeral ecdsa.PublicKey
}
// packdata is a structured representation of the DirEntry's Packdata field.
type packdata struct {
// sig is the signature with the primary owner key.
sig upspin.Signature
// sig2 is the signature with the previous owner key,
// to enable smoother key rotation.
sig2 upspin.Signature
// wrap is the file key, encoded with a set of reader keys.
wrap []wrappedKey
// blockSum is a checksum of the blocks.
blockSum []byte
}
// Marshal stores the binary-encoded version of packdata in the given slice,
// copying byte arrays to dst in the order declared in the struct definitions
// and prefixed with lengths using binary.PutVarint.
// A slice will be allocated and the pointer overwritten if *dst is too short.
func (pd *packdata) Marshal(dst *[]byte) error {
if n := packdataLen(len(pd.wrap)); len(*dst) < n {
*dst = make([]byte, n)
}
n := 0
// sig
n += packutil.PutBytes((*dst)[n:], pd.sig.R.Bytes())
n += packutil.PutBytes((*dst)[n:], pd.sig.S.Bytes())
// sig2
sig2 := pd.sig2
if sig2.R == nil {
zero := big.NewInt(0)
sig2 = upspin.Signature{R: zero, S: zero}
}
n += packutil.PutBytes((*dst)[n:], sig2.R.Bytes())
n += packutil.PutBytes((*dst)[n:], sig2.S.Bytes())
// wrap
n += binary.PutVarint((*dst)[n:], int64(len(pd.wrap)))
for _, w := range pd.wrap {
n += packutil.PutBytes((*dst)[n:], w.keyHash)
n += packutil.PutBytes((*dst)[n:], w.dkey)
n += packutil.PutBytes((*dst)[n:], w.nonce)
if w.ephemeral.X != nil {
n += packutil.PutBytes((*dst)[n:], w.ephemeral.X.Bytes())
} else {
n += packutil.PutBytes((*dst)[n:], nil)
}
if w.ephemeral.Y != nil {
n += packutil.PutBytes((*dst)[n:], w.ephemeral.Y.Bytes())
} else {
n += packutil.PutBytes((*dst)[n:], nil)
}
}
// blockSum
n += packutil.PutBytes((*dst)[n:], pd.blockSum)
*dst = (*dst)[:n]
return nil
}
// Unmarshal parses the given packdata slice and stores its contents in the
// receiver pd.
func (pd *packdata) Unmarshal(b []byte) error {
if len(b) == 0 {
return errors.Str("nil packdata")
}
n := 0
// sig
pd.sig.R = big.NewInt(0)
pd.sig.S = big.NewInt(0)
buf := make([]byte, marshalBufLen)
n += packutil.GetBytes(&buf, b[n:])
pd.sig.R.SetBytes(buf)
n += packutil.GetBytes(&buf, b[n:])
pd.sig.S.SetBytes(buf)
// sig2
pd.sig2.R = big.NewInt(0)
pd.sig2.S = big.NewInt(0)
n += packutil.GetBytes(&buf, b[n:])
pd.sig2.R.SetBytes(buf)
n += packutil.GetBytes(&buf, b[n:])
pd.sig2.S.SetBytes(buf)
// wrap
nwrap64, vlen := binary.Varint(b[n:])
n += vlen
nwrap := int(nwrap64)
if int64(nwrap) != nwrap64 {
return errors.Errorf("implausible number of wrapped keys: %d\n", nwrap64)
}
pd.wrap = make([]wrappedKey, nwrap)
for i := 0; i < nwrap; i++ {
var w wrappedKey
w.keyHash = make([]byte, sha256.Size)
w.dkey = make([]byte, aesKeyLen+gcmTagSize)
w.nonce = make([]byte, gcmStandardNonceSize)
w.ephemeral = ecdsa.PublicKey{X: big.NewInt(0), Y: big.NewInt(0)}
n += packutil.GetBytes(&w.keyHash, b[n:])
n += packutil.GetBytes(&w.dkey, b[n:])
n += packutil.GetBytes(&w.nonce, b[n:])
n += packutil.GetBytes(&buf, b[n:])
w.ephemeral.X.SetBytes(buf)
n += packutil.GetBytes(&buf, b[n:])
w.ephemeral.Y.SetBytes(buf)
if w.ephemeral.Y.BitLen() > 393 {
w.ephemeral.Curve = elliptic.P521()
} else if w.ephemeral.Y.BitLen() > 265 {
w.ephemeral.Curve = elliptic.P384()
} else {
w.ephemeral.Curve = elliptic.P256()
}
pd.wrap[i] = w
}
// blockSum
pd.blockSum = make([]byte, sha256.Size)
n += packutil.GetBytes(&pd.blockSum, b[n:])
if pd.blockSum == nil {
return errors.Str("block checksum is required")
}
return nil
}
// packdataLen returns the maximum length of a packdata slice for the given
// number of wrapped keys.
func packdataLen(nwrap int) int {
intLen := binary.MaxVarintLen64
// nWrappedKey is the size of a single encoded wrappedKey
nWrappedKey := intLen + sha256.Size // keyHash
nWrappedKey += intLen + aesKeyLen + gcmTagSize // dkey
nWrappedKey += intLen + gcmStandardNonceSize // nonce
nWrappedKey += 2 * (intLen + marshalBufLen) // ephemeral
n := 4 * (intLen + marshalBufLen) // (R,S) for (sig, sig2)
n += intLen // len(wrap)
n += nwrap * nWrappedKey
n += intLen + sha256.Size // blockSum
// n is commonly an overestimate since the big.Int used in p256 are
// about half the size of big.Int used in the assumed curve p521.
// At the time of writing:
// marshalBufLen=66 curve.Params().BitSize + 7) >> 3 for p521
// MaxVarintLen64=10
// sha256.Size=32
// aesKeyLen=32
// gcmTagSize=16
// gcmStandardNonceSize=12
// and therefore n = 356 + nwrap*274.
// On a 32-bit machine, this supports well over a million readers.
// We would redesign to use group keys long before that.
return n
}