xref: /aosp_15_r20/external/tink/go/streamingaead/subtle/noncebased/noncebased.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1*e7b1675dSTing-Kang Chang// Copyright 2020 Google LLC
2*e7b1675dSTing-Kang Chang//
3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang//
7*e7b1675dSTing-Kang Chang//      http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang//
9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang// limitations under the License.
14*e7b1675dSTing-Kang Chang//
15*e7b1675dSTing-Kang Chang////////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang
17*e7b1675dSTing-Kang Chang// Package noncebased provides a reusable streaming AEAD framework.
18*e7b1675dSTing-Kang Chang//
19*e7b1675dSTing-Kang Chang// It tackles the segment handling portions of the nonce based online
20*e7b1675dSTing-Kang Chang// encryption scheme proposed in "Online Authenticated-Encryption and its
21*e7b1675dSTing-Kang Chang// Nonce-Reuse Misuse-Resistance" by Hoang, Reyhanitabar, Rogaway and Vizár
22*e7b1675dSTing-Kang Chang// (https://eprint.iacr.org/2015/189.pdf).
23*e7b1675dSTing-Kang Chang//
24*e7b1675dSTing-Kang Chang// In this scheme, the format of a ciphertext is:
25*e7b1675dSTing-Kang Chang//
26*e7b1675dSTing-Kang Chang//   header || segment_0 || segment_1 || ... || segment_k.
27*e7b1675dSTing-Kang Chang//
28*e7b1675dSTing-Kang Chang// The format of header is:
29*e7b1675dSTing-Kang Chang//
30*e7b1675dSTing-Kang Chang//   headerLength || salt || nonce_prefix
31*e7b1675dSTing-Kang Chang//
32*e7b1675dSTing-Kang Chang// headerLength is 1 byte which documents the size of the header and can be
33*e7b1675dSTing-Kang Chang// obtained via HeaderLength(). In principle, headerLength is redundant
34*e7b1675dSTing-Kang Chang// information, since the length of the header can be determined from the key
35*e7b1675dSTing-Kang Chang// size.
36*e7b1675dSTing-Kang Chang//
37*e7b1675dSTing-Kang Chang// salt is a salt used in the key derivation.
38*e7b1675dSTing-Kang Chang//
39*e7b1675dSTing-Kang Chang// nonce_prefix is a prefix for all per-segment nonces.
40*e7b1675dSTing-Kang Chang//
41*e7b1675dSTing-Kang Chang// segment_i is the i-th segment of the ciphertext. The size of segment_1 ..
42*e7b1675dSTing-Kang Chang// segment_{k-1} is ciphertextSegmentSize. segment_0 is shorter, so that
43*e7b1675dSTing-Kang Chang// segment_0 plus additional data of size firstCiphertextSegmentOffset (e.g.
44*e7b1675dSTing-Kang Chang// the header) aligns with ciphertextSegmentSize.
45*e7b1675dSTing-Kang Chang//
46*e7b1675dSTing-Kang Chang// The first segment size will be:
47*e7b1675dSTing-Kang Chang//
48*e7b1675dSTing-Kang Chang//		ciphertextSegmentSize - HeaderLength() - firstCiphertextSegmentOffset.
49*e7b1675dSTing-Kang Changpackage noncebased
50*e7b1675dSTing-Kang Chang
51*e7b1675dSTing-Kang Changimport (
52*e7b1675dSTing-Kang Chang	"encoding/binary"
53*e7b1675dSTing-Kang Chang	"errors"
54*e7b1675dSTing-Kang Chang	"io"
55*e7b1675dSTing-Kang Chang	"math"
56*e7b1675dSTing-Kang Chang)
57*e7b1675dSTing-Kang Chang
58*e7b1675dSTing-Kang Changvar (
59*e7b1675dSTing-Kang Chang	// ErrNonceSizeTooShort indicates that the specified nonce size isn't large
60*e7b1675dSTing-Kang Chang	// enough to hold the nonce prefix, counter and last segment flag.
61*e7b1675dSTing-Kang Chang	ErrNonceSizeTooShort = errors.New("nonce size too short")
62*e7b1675dSTing-Kang Chang
63*e7b1675dSTing-Kang Chang	// ErrCiphertextSegmentTooShort indicates the the ciphertext segment being
64*e7b1675dSTing-Kang Chang	// processed is too short.
65*e7b1675dSTing-Kang Chang	ErrCiphertextSegmentTooShort = errors.New("ciphertext segment too short")
66*e7b1675dSTing-Kang Chang
67*e7b1675dSTing-Kang Chang	// ErrTooManySegments indicates that the ciphertext has too many segments.
68*e7b1675dSTing-Kang Chang	ErrTooManySegments = errors.New("too many segments")
69*e7b1675dSTing-Kang Chang)
70*e7b1675dSTing-Kang Chang
71*e7b1675dSTing-Kang Chang// SegmentEncrypter facilitates implementing various streaming AEAD encryption
72*e7b1675dSTing-Kang Chang// modes.
73*e7b1675dSTing-Kang Changtype SegmentEncrypter interface {
74*e7b1675dSTing-Kang Chang	EncryptSegment(segment, nonce []byte) ([]byte, error)
75*e7b1675dSTing-Kang Chang}
76*e7b1675dSTing-Kang Chang
77*e7b1675dSTing-Kang Chang// Writer provides a framework for ingesting plaintext data and
78*e7b1675dSTing-Kang Chang// writing encrypted data to the wrapped io.Writer. The scheme used for
79*e7b1675dSTing-Kang Chang// encrypting segments is specified by providing a SegmentEncrypter
80*e7b1675dSTing-Kang Chang// implementation.
81*e7b1675dSTing-Kang Changtype Writer struct {
82*e7b1675dSTing-Kang Chang	w                            io.Writer
83*e7b1675dSTing-Kang Chang	segmentEncrypter             SegmentEncrypter
84*e7b1675dSTing-Kang Chang	encryptedSegmentCnt          uint64
85*e7b1675dSTing-Kang Chang	firstCiphertextSegmentOffset int
86*e7b1675dSTing-Kang Chang	nonceSize                    int
87*e7b1675dSTing-Kang Chang	noncePrefix                  []byte
88*e7b1675dSTing-Kang Chang	plaintext                    []byte
89*e7b1675dSTing-Kang Chang	plaintextPos                 int
90*e7b1675dSTing-Kang Chang	ciphertext                   []byte
91*e7b1675dSTing-Kang Chang	closed                       bool
92*e7b1675dSTing-Kang Chang}
93*e7b1675dSTing-Kang Chang
94*e7b1675dSTing-Kang Chang// WriterParams contains the options for instantiating a Writer via NewWriter().
95*e7b1675dSTing-Kang Changtype WriterParams struct {
96*e7b1675dSTing-Kang Chang	// W is the underlying writer being wrapped.
97*e7b1675dSTing-Kang Chang	W io.Writer
98*e7b1675dSTing-Kang Chang
99*e7b1675dSTing-Kang Chang	// SegmentEncrypter provides a method for encrypting segments.
100*e7b1675dSTing-Kang Chang	SegmentEncrypter SegmentEncrypter
101*e7b1675dSTing-Kang Chang
102*e7b1675dSTing-Kang Chang	// NonceSize is the length of generated nonces. It must be at least 5 +
103*e7b1675dSTing-Kang Chang	// len(NoncePrefix). It can be longer, but longer nonces introduce more
104*e7b1675dSTing-Kang Chang	// overhead in the resultant ciphertext.
105*e7b1675dSTing-Kang Chang	NonceSize int
106*e7b1675dSTing-Kang Chang
107*e7b1675dSTing-Kang Chang	// NoncePrefix is a constant that all nonces throughout the ciphertext will
108*e7b1675dSTing-Kang Chang	// start with. It's length must be at least 5 bytes shorter than NonceSize.
109*e7b1675dSTing-Kang Chang	NoncePrefix []byte
110*e7b1675dSTing-Kang Chang
111*e7b1675dSTing-Kang Chang	// The size of the segments which the plaintext will be split into.
112*e7b1675dSTing-Kang Chang	PlaintextSegmentSize int
113*e7b1675dSTing-Kang Chang
114*e7b1675dSTing-Kang Chang	// FirstCiphertexSegmentOffset indicates where the ciphertext should begin in
115*e7b1675dSTing-Kang Chang	// W. This allows for the existence of overhead in the stream unrelated to
116*e7b1675dSTing-Kang Chang	// this encryption scheme.
117*e7b1675dSTing-Kang Chang	FirstCiphertextSegmentOffset int
118*e7b1675dSTing-Kang Chang}
119*e7b1675dSTing-Kang Chang
120*e7b1675dSTing-Kang Chang// NewWriter creates a new Writer instance.
121*e7b1675dSTing-Kang Changfunc NewWriter(params WriterParams) (*Writer, error) {
122*e7b1675dSTing-Kang Chang	if params.NonceSize-len(params.NoncePrefix) < 5 {
123*e7b1675dSTing-Kang Chang		return nil, ErrNonceSizeTooShort
124*e7b1675dSTing-Kang Chang	}
125*e7b1675dSTing-Kang Chang	return &Writer{
126*e7b1675dSTing-Kang Chang		w:                            params.W,
127*e7b1675dSTing-Kang Chang		segmentEncrypter:             params.SegmentEncrypter,
128*e7b1675dSTing-Kang Chang		nonceSize:                    params.NonceSize,
129*e7b1675dSTing-Kang Chang		noncePrefix:                  params.NoncePrefix,
130*e7b1675dSTing-Kang Chang		firstCiphertextSegmentOffset: params.FirstCiphertextSegmentOffset,
131*e7b1675dSTing-Kang Chang		plaintext:                    make([]byte, params.PlaintextSegmentSize),
132*e7b1675dSTing-Kang Chang	}, nil
133*e7b1675dSTing-Kang Chang}
134*e7b1675dSTing-Kang Chang
135*e7b1675dSTing-Kang Chang// Write encrypts passed data and passes the encrypted data to the underlying writer.
136*e7b1675dSTing-Kang Changfunc (w *Writer) Write(p []byte) (int, error) {
137*e7b1675dSTing-Kang Chang	if w.closed {
138*e7b1675dSTing-Kang Chang		return 0, errors.New("write on closed writer")
139*e7b1675dSTing-Kang Chang	}
140*e7b1675dSTing-Kang Chang
141*e7b1675dSTing-Kang Chang	pos := 0
142*e7b1675dSTing-Kang Chang	for {
143*e7b1675dSTing-Kang Chang		ptLim := len(w.plaintext)
144*e7b1675dSTing-Kang Chang		if w.encryptedSegmentCnt == 0 {
145*e7b1675dSTing-Kang Chang			ptLim -= w.firstCiphertextSegmentOffset
146*e7b1675dSTing-Kang Chang		}
147*e7b1675dSTing-Kang Chang		n := copy(w.plaintext[w.plaintextPos:ptLim], p[pos:])
148*e7b1675dSTing-Kang Chang		w.plaintextPos += n
149*e7b1675dSTing-Kang Chang		pos += n
150*e7b1675dSTing-Kang Chang		if pos == len(p) {
151*e7b1675dSTing-Kang Chang			break
152*e7b1675dSTing-Kang Chang		}
153*e7b1675dSTing-Kang Chang
154*e7b1675dSTing-Kang Chang		nonce, err := generateSegmentNonce(w.nonceSize, w.noncePrefix, w.encryptedSegmentCnt, false)
155*e7b1675dSTing-Kang Chang		if err != nil {
156*e7b1675dSTing-Kang Chang			return pos, err
157*e7b1675dSTing-Kang Chang		}
158*e7b1675dSTing-Kang Chang
159*e7b1675dSTing-Kang Chang		w.ciphertext, err = w.segmentEncrypter.EncryptSegment(w.plaintext[:ptLim], nonce)
160*e7b1675dSTing-Kang Chang		if err != nil {
161*e7b1675dSTing-Kang Chang			return pos, err
162*e7b1675dSTing-Kang Chang		}
163*e7b1675dSTing-Kang Chang
164*e7b1675dSTing-Kang Chang		if _, err := w.w.Write(w.ciphertext); err != nil {
165*e7b1675dSTing-Kang Chang			return pos, err
166*e7b1675dSTing-Kang Chang		}
167*e7b1675dSTing-Kang Chang
168*e7b1675dSTing-Kang Chang		w.plaintextPos = 0
169*e7b1675dSTing-Kang Chang		w.encryptedSegmentCnt++
170*e7b1675dSTing-Kang Chang	}
171*e7b1675dSTing-Kang Chang	return pos, nil
172*e7b1675dSTing-Kang Chang}
173*e7b1675dSTing-Kang Chang
174*e7b1675dSTing-Kang Chang// Close encrypts the remaining data, flushes it to the underlying writer and
175*e7b1675dSTing-Kang Chang// closes this writer.
176*e7b1675dSTing-Kang Changfunc (w *Writer) Close() error {
177*e7b1675dSTing-Kang Chang	if w.closed {
178*e7b1675dSTing-Kang Chang		return nil
179*e7b1675dSTing-Kang Chang	}
180*e7b1675dSTing-Kang Chang
181*e7b1675dSTing-Kang Chang	nonce, err := generateSegmentNonce(w.nonceSize, w.noncePrefix, w.encryptedSegmentCnt, true)
182*e7b1675dSTing-Kang Chang	if err != nil {
183*e7b1675dSTing-Kang Chang		return err
184*e7b1675dSTing-Kang Chang	}
185*e7b1675dSTing-Kang Chang
186*e7b1675dSTing-Kang Chang	w.ciphertext, err = w.segmentEncrypter.EncryptSegment(w.plaintext[:w.plaintextPos], nonce)
187*e7b1675dSTing-Kang Chang	if err != nil {
188*e7b1675dSTing-Kang Chang		return err
189*e7b1675dSTing-Kang Chang	}
190*e7b1675dSTing-Kang Chang
191*e7b1675dSTing-Kang Chang	if _, err := w.w.Write(w.ciphertext); err != nil {
192*e7b1675dSTing-Kang Chang		return err
193*e7b1675dSTing-Kang Chang	}
194*e7b1675dSTing-Kang Chang
195*e7b1675dSTing-Kang Chang	w.plaintextPos = 0
196*e7b1675dSTing-Kang Chang	w.encryptedSegmentCnt++
197*e7b1675dSTing-Kang Chang	w.closed = true
198*e7b1675dSTing-Kang Chang	return nil
199*e7b1675dSTing-Kang Chang}
200*e7b1675dSTing-Kang Chang
201*e7b1675dSTing-Kang Chang// SegmentDecrypter facilitates implementing various streaming AEAD encryption modes.
202*e7b1675dSTing-Kang Changtype SegmentDecrypter interface {
203*e7b1675dSTing-Kang Chang	DecryptSegment(segment, nonce []byte) ([]byte, error)
204*e7b1675dSTing-Kang Chang}
205*e7b1675dSTing-Kang Chang
206*e7b1675dSTing-Kang Chang// Reader facilitates the decryption of ciphertexts created using a Writer.
207*e7b1675dSTing-Kang Chang//
208*e7b1675dSTing-Kang Chang// The scheme used for decrypting segments is specified by providing a
209*e7b1675dSTing-Kang Chang// SegmentDecrypter implementation. The implementation must align
210*e7b1675dSTing-Kang Chang// with the SegmentEncrypter used in the Writer.
211*e7b1675dSTing-Kang Changtype Reader struct {
212*e7b1675dSTing-Kang Chang	r                            io.Reader
213*e7b1675dSTing-Kang Chang	segmentDecrypter             SegmentDecrypter
214*e7b1675dSTing-Kang Chang	decryptedSegmentCnt          uint64
215*e7b1675dSTing-Kang Chang	firstCiphertextSegmentOffset int
216*e7b1675dSTing-Kang Chang	nonceSize                    int
217*e7b1675dSTing-Kang Chang	noncePrefix                  []byte
218*e7b1675dSTing-Kang Chang	plaintext                    []byte
219*e7b1675dSTing-Kang Chang	plaintextPos                 int
220*e7b1675dSTing-Kang Chang	ciphertext                   []byte
221*e7b1675dSTing-Kang Chang	ciphertextPos                int
222*e7b1675dSTing-Kang Chang}
223*e7b1675dSTing-Kang Chang
224*e7b1675dSTing-Kang Chang// ReaderParams contains the options for instantiating a Reader via NewReader().
225*e7b1675dSTing-Kang Changtype ReaderParams struct {
226*e7b1675dSTing-Kang Chang	// R is the underlying reader being wrapped.
227*e7b1675dSTing-Kang Chang	R io.Reader
228*e7b1675dSTing-Kang Chang
229*e7b1675dSTing-Kang Chang	// SegmentDecrypter provides a method for decrypting segments.
230*e7b1675dSTing-Kang Chang	SegmentDecrypter SegmentDecrypter
231*e7b1675dSTing-Kang Chang
232*e7b1675dSTing-Kang Chang	// NonceSize is the length of generated nonces. It must match the NonceSize
233*e7b1675dSTing-Kang Chang	// of the Writer used to create the ciphertext.
234*e7b1675dSTing-Kang Chang	NonceSize int
235*e7b1675dSTing-Kang Chang
236*e7b1675dSTing-Kang Chang	// NoncePrefix is a constant that all nocnes throughout the ciphertext start
237*e7b1675dSTing-Kang Chang	// with. It's extracted from the header of the ciphertext.
238*e7b1675dSTing-Kang Chang	NoncePrefix []byte
239*e7b1675dSTing-Kang Chang
240*e7b1675dSTing-Kang Chang	// The size of the ciphertext segments.
241*e7b1675dSTing-Kang Chang	CiphertextSegmentSize int
242*e7b1675dSTing-Kang Chang
243*e7b1675dSTing-Kang Chang	// FirstCiphertexSegmentOffset indicates where the ciphertext actually begins
244*e7b1675dSTing-Kang Chang	// in R. This allows for the existence of overhead in the stream unrelated to
245*e7b1675dSTing-Kang Chang	// this encryption scheme.
246*e7b1675dSTing-Kang Chang	FirstCiphertextSegmentOffset int
247*e7b1675dSTing-Kang Chang}
248*e7b1675dSTing-Kang Chang
249*e7b1675dSTing-Kang Chang// NewReader creates a new Reader instance.
250*e7b1675dSTing-Kang Changfunc NewReader(params ReaderParams) (*Reader, error) {
251*e7b1675dSTing-Kang Chang	if params.NonceSize-len(params.NoncePrefix) < 5 {
252*e7b1675dSTing-Kang Chang		return nil, ErrNonceSizeTooShort
253*e7b1675dSTing-Kang Chang	}
254*e7b1675dSTing-Kang Chang	return &Reader{
255*e7b1675dSTing-Kang Chang		r:                            params.R,
256*e7b1675dSTing-Kang Chang		segmentDecrypter:             params.SegmentDecrypter,
257*e7b1675dSTing-Kang Chang		nonceSize:                    params.NonceSize,
258*e7b1675dSTing-Kang Chang		noncePrefix:                  params.NoncePrefix,
259*e7b1675dSTing-Kang Chang		firstCiphertextSegmentOffset: params.FirstCiphertextSegmentOffset,
260*e7b1675dSTing-Kang Chang
261*e7b1675dSTing-Kang Chang		// Allocate an extra byte to detect the last segment.
262*e7b1675dSTing-Kang Chang		ciphertext: make([]byte, params.CiphertextSegmentSize+1),
263*e7b1675dSTing-Kang Chang	}, nil
264*e7b1675dSTing-Kang Chang}
265*e7b1675dSTing-Kang Chang
266*e7b1675dSTing-Kang Chang// Read decrypts data from underlying reader and passes it to p.
267*e7b1675dSTing-Kang Changfunc (r *Reader) Read(p []byte) (int, error) {
268*e7b1675dSTing-Kang Chang	if r.plaintextPos < len(r.plaintext) {
269*e7b1675dSTing-Kang Chang		n := copy(p, r.plaintext[r.plaintextPos:])
270*e7b1675dSTing-Kang Chang		r.plaintextPos += n
271*e7b1675dSTing-Kang Chang		return n, nil
272*e7b1675dSTing-Kang Chang	}
273*e7b1675dSTing-Kang Chang
274*e7b1675dSTing-Kang Chang	r.plaintextPos = 0
275*e7b1675dSTing-Kang Chang
276*e7b1675dSTing-Kang Chang	ctLim := len(r.ciphertext)
277*e7b1675dSTing-Kang Chang	if r.decryptedSegmentCnt == 0 {
278*e7b1675dSTing-Kang Chang		ctLim -= r.firstCiphertextSegmentOffset
279*e7b1675dSTing-Kang Chang	}
280*e7b1675dSTing-Kang Chang	n, err := io.ReadFull(r.r, r.ciphertext[r.ciphertextPos:ctLim])
281*e7b1675dSTing-Kang Chang	if err != nil && err != io.ErrUnexpectedEOF {
282*e7b1675dSTing-Kang Chang		return 0, err
283*e7b1675dSTing-Kang Chang	}
284*e7b1675dSTing-Kang Chang
285*e7b1675dSTing-Kang Chang	var (
286*e7b1675dSTing-Kang Chang		lastSegment bool
287*e7b1675dSTing-Kang Chang		segment     int
288*e7b1675dSTing-Kang Chang	)
289*e7b1675dSTing-Kang Chang	if err != nil {
290*e7b1675dSTing-Kang Chang		lastSegment = true
291*e7b1675dSTing-Kang Chang		segment = r.ciphertextPos + n
292*e7b1675dSTing-Kang Chang	} else {
293*e7b1675dSTing-Kang Chang		segment = r.ciphertextPos + n - 1
294*e7b1675dSTing-Kang Chang	}
295*e7b1675dSTing-Kang Chang
296*e7b1675dSTing-Kang Chang	if segment < 0 {
297*e7b1675dSTing-Kang Chang		return 0, ErrCiphertextSegmentTooShort
298*e7b1675dSTing-Kang Chang	}
299*e7b1675dSTing-Kang Chang
300*e7b1675dSTing-Kang Chang	nonce, err := generateSegmentNonce(r.nonceSize, r.noncePrefix, r.decryptedSegmentCnt, lastSegment)
301*e7b1675dSTing-Kang Chang	if err != nil {
302*e7b1675dSTing-Kang Chang		return 0, err
303*e7b1675dSTing-Kang Chang	}
304*e7b1675dSTing-Kang Chang
305*e7b1675dSTing-Kang Chang	r.plaintext, err = r.segmentDecrypter.DecryptSegment(r.ciphertext[:segment], nonce)
306*e7b1675dSTing-Kang Chang	if err != nil {
307*e7b1675dSTing-Kang Chang		return 0, err
308*e7b1675dSTing-Kang Chang	}
309*e7b1675dSTing-Kang Chang
310*e7b1675dSTing-Kang Chang	// Copy 1 byte remainder to the beginning of ciphertext.
311*e7b1675dSTing-Kang Chang	if !lastSegment {
312*e7b1675dSTing-Kang Chang		remainderOffset := segment
313*e7b1675dSTing-Kang Chang		r.ciphertext[0] = r.ciphertext[remainderOffset]
314*e7b1675dSTing-Kang Chang		r.ciphertextPos = 1
315*e7b1675dSTing-Kang Chang	}
316*e7b1675dSTing-Kang Chang
317*e7b1675dSTing-Kang Chang	r.decryptedSegmentCnt++
318*e7b1675dSTing-Kang Chang
319*e7b1675dSTing-Kang Chang	n = copy(p, r.plaintext)
320*e7b1675dSTing-Kang Chang	r.plaintextPos = n
321*e7b1675dSTing-Kang Chang	return n, nil
322*e7b1675dSTing-Kang Chang}
323*e7b1675dSTing-Kang Chang
324*e7b1675dSTing-Kang Chang// generateSegmentNonce returns a nonce for a segment.
325*e7b1675dSTing-Kang Chang//
326*e7b1675dSTing-Kang Chang// The format of the nonce is:
327*e7b1675dSTing-Kang Chang//
328*e7b1675dSTing-Kang Chang//   nonce_prefix || ctr || last_block.
329*e7b1675dSTing-Kang Chang//
330*e7b1675dSTing-Kang Chang// nonce_prefix is a constant prefix used throughout the whole ciphertext.
331*e7b1675dSTing-Kang Chang//
332*e7b1675dSTing-Kang Chang// The ctr is a 32 bit counter.
333*e7b1675dSTing-Kang Chang//
334*e7b1675dSTing-Kang Chang// last_block is 1 byte which is set to 1 for the last segment and 0
335*e7b1675dSTing-Kang Chang// otherwise.
336*e7b1675dSTing-Kang Changfunc generateSegmentNonce(size int, prefix []byte, segmentNum uint64, last bool) ([]byte, error) {
337*e7b1675dSTing-Kang Chang	if segmentNum >= math.MaxUint32 {
338*e7b1675dSTing-Kang Chang		return nil, ErrTooManySegments
339*e7b1675dSTing-Kang Chang	}
340*e7b1675dSTing-Kang Chang
341*e7b1675dSTing-Kang Chang	nonce := make([]byte, size)
342*e7b1675dSTing-Kang Chang	copy(nonce, prefix)
343*e7b1675dSTing-Kang Chang	offset := len(prefix)
344*e7b1675dSTing-Kang Chang	binary.BigEndian.PutUint32(nonce[offset:], uint32(segmentNum))
345*e7b1675dSTing-Kang Chang	offset += 4
346*e7b1675dSTing-Kang Chang	if last {
347*e7b1675dSTing-Kang Chang		nonce[offset] = 1
348*e7b1675dSTing-Kang Chang	}
349*e7b1675dSTing-Kang Chang	return nonce, nil
350*e7b1675dSTing-Kang Chang}
351