]> Cypherpunks.ru repositories - nncp.git/blobdiff - src/pkt.go
Streamed NNCPE format
[nncp.git] / src / pkt.go
index 6eb4ef201a5484b5a7a4e6421a4076dc22d8181f..a1993e7a8ed98334e2c96dc14e22cc28d73235a1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2020 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -21,29 +21,29 @@ import (
        "bytes"
        "crypto/cipher"
        "crypto/rand"
-       "encoding/binary"
        "errors"
        "io"
 
        xdr "github.com/davecgh/go-xdr/xdr2"
-       "golang.org/x/crypto/blake2b"
        "golang.org/x/crypto/chacha20poly1305"
        "golang.org/x/crypto/curve25519"
        "golang.org/x/crypto/ed25519"
        "golang.org/x/crypto/nacl/box"
        "golang.org/x/crypto/poly1305"
+       "lukechampine.com/blake3"
 )
 
 type PktType uint8
 
 const (
        EncBlkSize = 128 * (1 << 10)
-       KDFXOFSize = chacha20poly1305.KeySize * 2
 
-       PktTypeFile PktType = iota
-       PktTypeFreq PktType = iota
-       PktTypeExec PktType = iota
-       PktTypeTrns PktType = iota
+       PktTypeFile    PktType = iota
+       PktTypeFreq    PktType = iota
+       PktTypeExec    PktType = iota
+       PktTypeTrns    PktType = iota
+       PktTypeExecFat PktType = iota
+       PktTypeArea    PktType = iota
 
        MaxPathSize = 1<<8 - 1
 
@@ -51,14 +51,17 @@ const (
 )
 
 var (
-       MagicNNCPPv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 3}
-       MagicNNCPEv4 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 4}
-       BadMagic     error   = errors.New("Unknown magic number")
-       BadPktType   error   = errors.New("Unknown packet type")
+       BadPktType error = errors.New("Unknown packet type")
+
+       DeriveKeyFullCtx = string(MagicNNCPEv6.B[:]) + " FULL"
+       DeriveKeySizeCtx = string(MagicNNCPEv6.B[:]) + " SIZE"
+       DeriveKeyPadCtx  = string(MagicNNCPEv6.B[:]) + " PAD"
 
        PktOverhead     int64
        PktEncOverhead  int64
-       PktSizeOverhead int64 = 8 + poly1305.TagSize
+       PktSizeOverhead int64
+
+       TooBig = errors.New("Too big than allowed")
 )
 
 type Pkt struct {
@@ -66,7 +69,7 @@ type Pkt struct {
        Type    PktType
        Nice    uint8
        PathLen uint8
-       Path    *[MaxPathSize]byte
+       Path    [MaxPathSize]byte
 }
 
 type PktTbs struct {
@@ -74,7 +77,7 @@ type PktTbs struct {
        Nice      uint8
        Sender    *NodeId
        Recipient *NodeId
-       ExchPub   *[32]byte
+       ExchPub   [32]byte
 }
 
 type PktEnc struct {
@@ -82,16 +85,32 @@ type PktEnc struct {
        Nice      uint8
        Sender    *NodeId
        Recipient *NodeId
-       ExchPub   *[32]byte
-       Sign      *[ed25519.SignatureSize]byte
+       ExchPub   [32]byte
+       Sign      [ed25519.SignatureSize]byte
 }
 
-func init() {
+type PktSize struct {
+       Payload uint64
+       Pad     uint64
+}
+
+func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
+       if len(path) > MaxPathSize {
+               return nil, errors.New("Too long path")
+       }
        pkt := Pkt{
-               Type: PktTypeFile,
-               Path: new([MaxPathSize]byte),
+               Magic:   MagicNNCPPv3.B,
+               Type:    typ,
+               Nice:    nice,
+               PathLen: uint8(len(path)),
        }
+       copy(pkt.Path[:], path)
+       return &pkt, nil
+}
+
+func init() {
        var buf bytes.Buffer
+       pkt := Pkt{Type: PktTypeFile}
        n, err := xdr.Marshal(&buf, pkt)
        if err != nil {
                panic(err)
@@ -99,89 +118,62 @@ func init() {
        PktOverhead = int64(n)
        buf.Reset()
 
-       dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
+       dummyId, err := NodeIdFromString(DummyB32Id)
        if err != nil {
                panic(err)
        }
        pktEnc := PktEnc{
-               Magic:     MagicNNCPEv4,
-               Nice:      123,
+               Magic:     MagicNNCPEv6.B,
                Sender:    dummyId,
                Recipient: dummyId,
-               ExchPub:   new([32]byte),
-               Sign:      new([ed25519.SignatureSize]byte),
        }
        n, err = xdr.Marshal(&buf, pktEnc)
        if err != nil {
                panic(err)
        }
        PktEncOverhead = int64(n)
-}
+       buf.Reset()
 
-func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
-       if len(path) > MaxPathSize {
-               return nil, errors.New("Too long path")
+       size := PktSize{}
+       n, err = xdr.Marshal(&buf, size)
+       if err != nil {
+               panic(err)
        }
-       pkt := Pkt{
-               Magic:   MagicNNCPPv3,
-               Type:    typ,
-               Nice:    nice,
-               PathLen: uint8(len(path)),
-               Path:    new([MaxPathSize]byte),
+       PktSizeOverhead = int64(n)
+}
+
+func ctrIncr(b []byte) {
+       for i := len(b) - 1; i >= 0; i-- {
+               b[i]++
+               if b[i] != 0 {
+                       return
+               }
        }
-       copy(pkt.Path[:], path)
-       return &pkt, nil
+       panic("counter overflow")
 }
 
-func aeadProcess(
-       aead cipher.AEAD,
-       nonce []byte,
-       doEncrypt bool,
-       r io.Reader,
-       w io.Writer,
-) (int, error) {
-       var blkCtr uint64
-       ciphCtr := nonce[len(nonce)-8:]
-       buf := make([]byte, EncBlkSize+aead.Overhead())
-       var toRead []byte
-       var toWrite []byte
-       var n int
-       var readBytes int
-       var err error
-       if doEncrypt {
-               toRead = buf[:EncBlkSize]
-       } else {
-               toRead = buf
+func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
+       tbs := PktTbs{
+               Magic:     MagicNNCPEv6.B,
+               Nice:      pktEnc.Nice,
+               Sender:    their.Id,
+               Recipient: our.Id,
+               ExchPub:   pktEnc.ExchPub,
        }
-       for {
-               n, err = io.ReadFull(r, toRead)
-               if err != nil {
-                       if err == io.EOF {
-                               break
-                       }
-                       if err != io.ErrUnexpectedEOF {
-                               return readBytes + n, err
-                       }
-               }
-               readBytes += n
-               blkCtr++
-               binary.BigEndian.PutUint64(ciphCtr, blkCtr)
-               if doEncrypt {
-                       toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
-               } else {
-                       toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
-                       if err != nil {
-                               return readBytes, err
-                       }
-               }
-               if _, err = w.Write(toWrite); err != nil {
-                       return readBytes, err
-               }
+       var tbsBuf bytes.Buffer
+       if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
+               panic(err)
        }
-       return readBytes, nil
+       return tbsBuf.Bytes()
+}
+
+func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
+       tbs := TbsPrepare(our, their, pktEnc)
+       return tbs, ed25519.Verify(their.SignPub, tbs, pktEnc.Sign[:]), nil
 }
 
 func sizeWithTags(size int64) (fullSize int64) {
+       size += PktSizeOverhead
        fullSize = size + (size/EncBlkSize)*poly1305.TagSize
        if size%EncBlkSize != 0 {
                fullSize += poly1305.TagSize
@@ -189,178 +181,346 @@ func sizeWithTags(size int64) (fullSize int64) {
        return
 }
 
+func sizePadCalc(sizePayload, minSize int64, wrappers int) (sizePad int64) {
+       expectedSize := sizePayload - PktOverhead
+       for i := 0; i < wrappers; i++ {
+               expectedSize = PktEncOverhead + sizeWithTags(PktOverhead+expectedSize)
+       }
+       sizePad = minSize - expectedSize
+       if sizePad < 0 {
+               sizePad = 0
+       }
+       return
+}
+
 func PktEncWrite(
-       our *NodeOur,
-       their *Node,
-       pkt *Pkt,
-       nice uint8,
-       size, padSize int64,
-       data io.Reader,
-       out io.Writer,
-) error {
-       pubEph, prvEph, err := box.GenerateKey(rand.Reader)
+       our *NodeOur, their *Node,
+       pkt *Pkt, nice uint8,
+       minSize, maxSize int64, wrappers int,
+       r io.Reader, w io.Writer,
+) (pktEncRaw []byte, size int64, err error) {
+       pub, prv, err := box.GenerateKey(rand.Reader)
        if err != nil {
-               return err
+               return nil, 0, err
        }
-       var pktBuf bytes.Buffer
-       if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
-               return err
+
+       var buf bytes.Buffer
+       _, err = xdr.Marshal(&buf, pkt)
+       if err != nil {
+               return
        }
+       pktRaw := make([]byte, buf.Len())
+       copy(pktRaw, buf.Bytes())
+       buf.Reset()
+
        tbs := PktTbs{
-               Magic:     MagicNNCPEv4,
+               Magic:     MagicNNCPEv6.B,
                Nice:      nice,
                Sender:    our.Id,
                Recipient: their.Id,
-               ExchPub:   pubEph,
+               ExchPub:   *pub,
        }
-       var tbsBuf bytes.Buffer
-       if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
-               return err
+       _, err = xdr.Marshal(&buf, &tbs)
+       if err != nil {
+               return
        }
        signature := new([ed25519.SignatureSize]byte)
-       copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
+       copy(signature[:], ed25519.Sign(our.SignPrv, buf.Bytes()))
+       ad := blake3.Sum256(buf.Bytes())
+       buf.Reset()
+
        pktEnc := PktEnc{
-               Magic:     MagicNNCPEv4,
+               Magic:     MagicNNCPEv6.B,
                Nice:      nice,
                Sender:    our.Id,
                Recipient: their.Id,
-               ExchPub:   pubEph,
-               Sign:      signature,
+               ExchPub:   *pub,
+               Sign:      *signature,
        }
-       if _, err = xdr.Marshal(out, &pktEnc); err != nil {
-               return err
-       }
-       sharedKey := new([32]byte)
-       curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
-       kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
+       _, err = xdr.Marshal(&buf, &pktEnc)
        if err != nil {
-               return err
+               return
        }
-       if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
-               return err
+       pktEncRaw = make([]byte, buf.Len())
+       copy(pktEncRaw, buf.Bytes())
+       buf.Reset()
+       _, err = w.Write(pktEncRaw)
+       if err != nil {
+               return
        }
 
-       key := make([]byte, chacha20poly1305.KeySize)
-       if _, err = io.ReadFull(kdf, key); err != nil {
-               return err
+       sharedKey := new([32]byte)
+       curve25519.ScalarMult(sharedKey, prv, their.ExchPub)
+       keyFull := make([]byte, chacha20poly1305.KeySize)
+       keySize := make([]byte, chacha20poly1305.KeySize)
+       blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
+       blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
+       aeadFull, err := chacha20poly1305.New(keyFull)
+       if err != nil {
+               return
        }
-       aead, err := chacha20poly1305.New(key)
+       aeadSize, err := chacha20poly1305.New(keySize)
        if err != nil {
-               return err
+               return
        }
-       nonce := make([]byte, aead.NonceSize())
+       nonce := make([]byte, aeadFull.NonceSize())
 
-       fullSize := pktBuf.Len() + int(size)
-       sizeBuf := make([]byte, 8+aead.Overhead())
-       binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
-       if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
-               return err
+       data := make([]byte, EncBlkSize, EncBlkSize+aeadFull.Overhead())
+       mr := io.MultiReader(bytes.NewReader(pktRaw), r)
+       var sizePayload int64
+       var n int
+       var ct []byte
+       for {
+               n, err = io.ReadFull(mr, data)
+               sizePayload += int64(n)
+               if sizePayload > maxSize {
+                       err = TooBig
+                       return
+               }
+               if err == nil {
+                       ct = aeadFull.Seal(data[:0], nonce, data[:n], ad[:])
+                       _, err = w.Write(ct)
+                       if err != nil {
+                               return
+                       }
+                       ctrIncr(nonce)
+                       continue
+               }
+               if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
+                       return
+               }
+               break
        }
 
-       lr := io.LimitedReader{R: data, N: size}
-       mr := io.MultiReader(&pktBuf, &lr)
-       written, err := aeadProcess(aead, nonce, true, mr, out)
+       sizePad := sizePadCalc(sizePayload, minSize, wrappers)
+       _, err = xdr.Marshal(&buf, &PktSize{uint64(sizePayload), uint64(sizePad)})
        if err != nil {
-               return err
-       }
-       if written != fullSize {
-               return io.ErrUnexpectedEOF
+               return
        }
-       if padSize > 0 {
-               if _, err = io.ReadFull(kdf, key); err != nil {
-                       return err
-               }
-               kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
+
+       var aeadLast cipher.AEAD
+       if n+int(PktSizeOverhead) > EncBlkSize {
+               left := make([]byte, (n+int(PktSizeOverhead))-EncBlkSize)
+               copy(left, data[n-len(left):])
+               copy(data[PktSizeOverhead:], data[:n-len(left)])
+               copy(data[:PktSizeOverhead], buf.Bytes())
+               ct = aeadSize.Seal(data[:0], nonce, data[:EncBlkSize], ad[:])
+               _, err = w.Write(ct)
                if err != nil {
-                       return err
-               }
-               if _, err = io.CopyN(out, kdf, padSize); err != nil {
-                       return err
+                       return
                }
+               ctrIncr(nonce)
+               copy(data, left)
+               n = len(left)
+               aeadLast = aeadFull
+       } else {
+               copy(data[PktSizeOverhead:], data[:n])
+               copy(data[:PktSizeOverhead], buf.Bytes())
+               n += int(PktSizeOverhead)
+               aeadLast = aeadSize
        }
-       return nil
-}
 
-func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
-       tbs := PktTbs{
-               Magic:     MagicNNCPEv4,
-               Nice:      pktEnc.Nice,
-               Sender:    their.Id,
-               Recipient: our.Id,
-               ExchPub:   pktEnc.ExchPub,
+       var sizeBlockPadded int
+       var sizePadLeft int64
+       if sizePad > EncBlkSize-int64(n) {
+               sizeBlockPadded = EncBlkSize
+               sizePadLeft = sizePad - (EncBlkSize - int64(n))
+       } else {
+               sizeBlockPadded = n + int(sizePad)
+               sizePadLeft = 0
        }
-       var tbsBuf bytes.Buffer
-       if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
-               return false, err
+       for i := n; i < sizeBlockPadded; i++ {
+               data[i] = 0
+       }
+       ct = aeadLast.Seal(data[:0], nonce, data[:sizeBlockPadded], ad[:])
+       _, err = w.Write(ct)
+       if err != nil {
+               return
        }
-       return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
+
+       size = sizePayload
+       if sizePadLeft > 0 {
+               keyPad := make([]byte, chacha20poly1305.KeySize)
+               blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
+               _, err = io.CopyN(w, blake3.New(32, keyPad).XOF(), sizePadLeft)
+       }
+       return
 }
 
 func PktEncRead(
-       our *NodeOur,
-       nodes map[NodeId]*Node,
-       data io.Reader,
-       out io.Writer,
-) (*Node, int64, error) {
+       our *NodeOur, nodes map[NodeId]*Node,
+       r io.Reader, w io.Writer,
+       signatureVerify bool,
+       sharedKeyCached []byte,
+) (sharedKey []byte, their *Node, size int64, err error) {
        var pktEnc PktEnc
-       _, err := xdr.Unmarshal(data, &pktEnc)
+       _, err = xdr.Unmarshal(r, &pktEnc)
        if err != nil {
-               return nil, 0, err
+               return
+       }
+       switch pktEnc.Magic {
+       case MagicNNCPEv1.B:
+               err = MagicNNCPEv1.TooOld()
+       case MagicNNCPEv2.B:
+               err = MagicNNCPEv2.TooOld()
+       case MagicNNCPEv3.B:
+               err = MagicNNCPEv3.TooOld()
+       case MagicNNCPEv4.B:
+               err = MagicNNCPEv4.TooOld()
+       case MagicNNCPEv5.B:
+               err = MagicNNCPEv5.TooOld()
+       case MagicNNCPEv6.B:
+       default:
+               err = BadMagic
        }
-       if pktEnc.Magic != MagicNNCPEv4 {
-               return nil, 0, BadMagic
-       }
-       their, known := nodes[*pktEnc.Sender]
-       if !known {
-               return nil, 0, errors.New("Unknown sender")
+       if err != nil {
+               return
        }
        if *pktEnc.Recipient != *our.Id {
-               return nil, 0, errors.New("Invalid recipient")
+               err = errors.New("Invalid recipient")
+               return
        }
-       verified, err := TbsVerify(our, their, &pktEnc)
-       if err != nil {
-               return nil, 0, err
+
+       var tbsRaw []byte
+       if signatureVerify {
+               their = nodes[*pktEnc.Sender]
+               if their == nil {
+                       err = errors.New("Unknown sender")
+                       return
+               }
+               var verified bool
+               tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
+               if err != nil {
+                       return
+               }
+               if !verified {
+                       err = errors.New("Invalid signature")
+                       return
+               }
+       } else {
+               tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
        }
-       if !verified {
-               return their, 0, errors.New("Invalid signature")
+       ad := blake3.Sum256(tbsRaw)
+       if sharedKeyCached == nil {
+               key := new([32]byte)
+               curve25519.ScalarMult(key, our.ExchPrv, &pktEnc.ExchPub)
+               sharedKey = key[:]
+       } else {
+               sharedKey = sharedKeyCached
        }
-       sharedKey := new([32]byte)
-       curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
-       kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
+
+       keyFull := make([]byte, chacha20poly1305.KeySize)
+       keySize := make([]byte, chacha20poly1305.KeySize)
+       blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
+       blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
+       aeadFull, err := chacha20poly1305.New(keyFull)
        if err != nil {
-               return their, 0, err
+               return
        }
-       if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
-               return their, 0, err
+       aeadSize, err := chacha20poly1305.New(keySize)
+       if err != nil {
+               return
        }
+       nonce := make([]byte, aeadFull.NonceSize())
 
-       key := make([]byte, chacha20poly1305.KeySize)
-       if _, err = io.ReadFull(kdf, key); err != nil {
-               return their, 0, err
+       ct := make([]byte, EncBlkSize+aeadFull.Overhead())
+       pt := make([]byte, EncBlkSize)
+       var n int
+FullRead:
+       for {
+               n, err = io.ReadFull(r, ct)
+               switch err {
+               case nil:
+                       pt, err = aeadFull.Open(pt[:0], nonce, ct, ad[:])
+                       if err != nil {
+                               break FullRead
+                       }
+                       size += EncBlkSize
+                       _, err = w.Write(pt)
+                       if err != nil {
+                               return
+                       }
+                       ctrIncr(nonce)
+                       continue
+               case io.ErrUnexpectedEOF:
+                       break FullRead
+               default:
+                       return
+               }
        }
-       aead, err := chacha20poly1305.New(key)
+
+       pt, err = aeadSize.Open(pt[:0], nonce, ct[:n], ad[:])
        if err != nil {
-               return their, 0, err
+               return
        }
-       nonce := make([]byte, aead.NonceSize())
+       var pktSize PktSize
+       _, err = xdr.Unmarshal(bytes.NewReader(pt), &pktSize)
+       if err != nil {
+               return
+       }
+       pt = pt[PktSizeOverhead:]
 
-       sizeBuf := make([]byte, 8+aead.Overhead())
-       if _, err = io.ReadFull(data, sizeBuf); err != nil {
-               return their, 0, err
+       left := int64(pktSize.Payload) - size
+       for left > int64(len(pt)) {
+               size += int64(len(pt))
+               left -= int64(len(pt))
+               _, err = w.Write(pt)
+               if err != nil {
+                       return
+               }
+               n, err = io.ReadFull(r, ct)
+               if err != nil && err != io.ErrUnexpectedEOF {
+                       return
+               }
+               ctrIncr(nonce)
+               pt, err = aeadFull.Open(pt[:0], nonce, ct[:n], ad[:])
+               if err != nil {
+                       return
+               }
        }
-       sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
+       size += left
+       _, err = w.Write(pt[:left])
        if err != nil {
-               return their, 0, err
+               return
        }
-       size := int64(binary.BigEndian.Uint64(sizeBuf))
+       pt = pt[left:]
 
-       lr := io.LimitedReader{R: data, N: size}
-       written, err := aeadProcess(aead, nonce, false, &lr, out)
-       if err != nil {
-               return their, int64(written), err
+       if pktSize.Pad < uint64(len(pt)) {
+               err = errors.New("unexpected pad")
+               return
+       }
+       for i := 0; i < len(pt); i++ {
+               if pt[i] != 0 {
+                       err = errors.New("non-zero pad byte")
+                       return
+               }
        }
-       if written != int(size) {
-               return their, int64(written), io.ErrUnexpectedEOF
+       sizePad := int64(pktSize.Pad) - int64(len(pt))
+       if sizePad == 0 {
+               return
        }
-       return their, size, nil
+
+       keyPad := make([]byte, chacha20poly1305.KeySize)
+       blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
+       xof := blake3.New(32, keyPad).XOF()
+       pt = make([]byte, len(ct))
+       for sizePad > 0 {
+               n, err = io.ReadFull(r, ct)
+               if err != nil && err != io.ErrUnexpectedEOF {
+                       return
+               }
+               _, err = io.ReadFull(xof, pt[:n])
+               if err != nil {
+                       panic(err)
+               }
+               if bytes.Compare(ct[:n], pt[:n]) != 0 {
+                       err = errors.New("wrong pad value")
+                       return
+               }
+               sizePad -= int64(n)
+       }
+       if sizePad < 0 {
+               err = errors.New("excess pad")
+       }
+       return
 }