mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-11-30 16:46:27 +00:00
591 lines
15 KiB
Go
591 lines
15 KiB
Go
|
// Copyright 2011 The Go 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 packet implements parsing and serialization of OpenPGP packets, as
|
||
|
// specified in RFC 4880.
|
||
|
//
|
||
|
// Deprecated: this package is unmaintained except for security fixes. New
|
||
|
// applications should consider a more focused, modern alternative to OpenPGP
|
||
|
// for their specific task. If you are required to interoperate with OpenPGP
|
||
|
// systems and need a maintained package, consider a community fork.
|
||
|
// See https://golang.org/issue/44226.
|
||
|
package packet // import "golang.org/x/crypto/openpgp/packet"
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"crypto/aes"
|
||
|
"crypto/cipher"
|
||
|
"crypto/des"
|
||
|
"crypto/rsa"
|
||
|
"io"
|
||
|
"math/big"
|
||
|
"math/bits"
|
||
|
|
||
|
"golang.org/x/crypto/cast5"
|
||
|
"golang.org/x/crypto/openpgp/errors"
|
||
|
)
|
||
|
|
||
|
// readFull is the same as io.ReadFull except that reading zero bytes returns
|
||
|
// ErrUnexpectedEOF rather than EOF.
|
||
|
func readFull(r io.Reader, buf []byte) (n int, err error) {
|
||
|
n, err = io.ReadFull(r, buf)
|
||
|
if err == io.EOF {
|
||
|
err = io.ErrUnexpectedEOF
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2.
|
||
|
func readLength(r io.Reader) (length int64, isPartial bool, err error) {
|
||
|
var buf [4]byte
|
||
|
_, err = readFull(r, buf[:1])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
switch {
|
||
|
case buf[0] < 192:
|
||
|
length = int64(buf[0])
|
||
|
case buf[0] < 224:
|
||
|
length = int64(buf[0]-192) << 8
|
||
|
_, err = readFull(r, buf[0:1])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
length += int64(buf[0]) + 192
|
||
|
case buf[0] < 255:
|
||
|
length = int64(1) << (buf[0] & 0x1f)
|
||
|
isPartial = true
|
||
|
default:
|
||
|
_, err = readFull(r, buf[0:4])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
length = int64(buf[0])<<24 |
|
||
|
int64(buf[1])<<16 |
|
||
|
int64(buf[2])<<8 |
|
||
|
int64(buf[3])
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths.
|
||
|
// The continuation lengths are parsed and removed from the stream and EOF is
|
||
|
// returned at the end of the packet. See RFC 4880, section 4.2.2.4.
|
||
|
type partialLengthReader struct {
|
||
|
r io.Reader
|
||
|
remaining int64
|
||
|
isPartial bool
|
||
|
}
|
||
|
|
||
|
func (r *partialLengthReader) Read(p []byte) (n int, err error) {
|
||
|
for r.remaining == 0 {
|
||
|
if !r.isPartial {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
r.remaining, r.isPartial, err = readLength(r.r)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
toRead := int64(len(p))
|
||
|
if toRead > r.remaining {
|
||
|
toRead = r.remaining
|
||
|
}
|
||
|
|
||
|
n, err = r.r.Read(p[:int(toRead)])
|
||
|
r.remaining -= int64(n)
|
||
|
if n < int(toRead) && err == io.EOF {
|
||
|
err = io.ErrUnexpectedEOF
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// partialLengthWriter writes a stream of data using OpenPGP partial lengths.
|
||
|
// See RFC 4880, section 4.2.2.4.
|
||
|
type partialLengthWriter struct {
|
||
|
w io.WriteCloser
|
||
|
lengthByte [1]byte
|
||
|
sentFirst bool
|
||
|
buf []byte
|
||
|
}
|
||
|
|
||
|
// RFC 4880 4.2.2.4: the first partial length MUST be at least 512 octets long.
|
||
|
const minFirstPartialWrite = 512
|
||
|
|
||
|
func (w *partialLengthWriter) Write(p []byte) (n int, err error) {
|
||
|
off := 0
|
||
|
if !w.sentFirst {
|
||
|
if len(w.buf) > 0 || len(p) < minFirstPartialWrite {
|
||
|
off = len(w.buf)
|
||
|
w.buf = append(w.buf, p...)
|
||
|
if len(w.buf) < minFirstPartialWrite {
|
||
|
return len(p), nil
|
||
|
}
|
||
|
p = w.buf
|
||
|
w.buf = nil
|
||
|
}
|
||
|
w.sentFirst = true
|
||
|
}
|
||
|
|
||
|
power := uint8(30)
|
||
|
for len(p) > 0 {
|
||
|
l := 1 << power
|
||
|
if len(p) < l {
|
||
|
power = uint8(bits.Len32(uint32(len(p)))) - 1
|
||
|
l = 1 << power
|
||
|
}
|
||
|
w.lengthByte[0] = 224 + power
|
||
|
_, err = w.w.Write(w.lengthByte[:])
|
||
|
if err == nil {
|
||
|
var m int
|
||
|
m, err = w.w.Write(p[:l])
|
||
|
n += m
|
||
|
}
|
||
|
if err != nil {
|
||
|
if n < off {
|
||
|
return 0, err
|
||
|
}
|
||
|
return n - off, err
|
||
|
}
|
||
|
p = p[l:]
|
||
|
}
|
||
|
return n - off, nil
|
||
|
}
|
||
|
|
||
|
func (w *partialLengthWriter) Close() error {
|
||
|
if len(w.buf) > 0 {
|
||
|
// In this case we can't send a 512 byte packet.
|
||
|
// Just send what we have.
|
||
|
p := w.buf
|
||
|
w.sentFirst = true
|
||
|
w.buf = nil
|
||
|
if _, err := w.Write(p); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
w.lengthByte[0] = 0
|
||
|
_, err := w.w.Write(w.lengthByte[:])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return w.w.Close()
|
||
|
}
|
||
|
|
||
|
// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the
|
||
|
// underlying Reader returns EOF before the limit has been reached.
|
||
|
type spanReader struct {
|
||
|
r io.Reader
|
||
|
n int64
|
||
|
}
|
||
|
|
||
|
func (l *spanReader) Read(p []byte) (n int, err error) {
|
||
|
if l.n <= 0 {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
if int64(len(p)) > l.n {
|
||
|
p = p[0:l.n]
|
||
|
}
|
||
|
n, err = l.r.Read(p)
|
||
|
l.n -= int64(n)
|
||
|
if l.n > 0 && err == io.EOF {
|
||
|
err = io.ErrUnexpectedEOF
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// readHeader parses a packet header and returns an io.Reader which will return
|
||
|
// the contents of the packet. See RFC 4880, section 4.2.
|
||
|
func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) {
|
||
|
var buf [4]byte
|
||
|
_, err = io.ReadFull(r, buf[:1])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if buf[0]&0x80 == 0 {
|
||
|
err = errors.StructuralError("tag byte does not have MSB set")
|
||
|
return
|
||
|
}
|
||
|
if buf[0]&0x40 == 0 {
|
||
|
// Old format packet
|
||
|
tag = packetType((buf[0] & 0x3f) >> 2)
|
||
|
lengthType := buf[0] & 3
|
||
|
if lengthType == 3 {
|
||
|
length = -1
|
||
|
contents = r
|
||
|
return
|
||
|
}
|
||
|
lengthBytes := 1 << lengthType
|
||
|
_, err = readFull(r, buf[0:lengthBytes])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
for i := 0; i < lengthBytes; i++ {
|
||
|
length <<= 8
|
||
|
length |= int64(buf[i])
|
||
|
}
|
||
|
contents = &spanReader{r, length}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// New format packet
|
||
|
tag = packetType(buf[0] & 0x3f)
|
||
|
length, isPartial, err := readLength(r)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if isPartial {
|
||
|
contents = &partialLengthReader{
|
||
|
remaining: length,
|
||
|
isPartial: true,
|
||
|
r: r,
|
||
|
}
|
||
|
length = -1
|
||
|
} else {
|
||
|
contents = &spanReader{r, length}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section
|
||
|
// 4.2.
|
||
|
func serializeHeader(w io.Writer, ptype packetType, length int) (err error) {
|
||
|
var buf [6]byte
|
||
|
var n int
|
||
|
|
||
|
buf[0] = 0x80 | 0x40 | byte(ptype)
|
||
|
if length < 192 {
|
||
|
buf[1] = byte(length)
|
||
|
n = 2
|
||
|
} else if length < 8384 {
|
||
|
length -= 192
|
||
|
buf[1] = 192 + byte(length>>8)
|
||
|
buf[2] = byte(length)
|
||
|
n = 3
|
||
|
} else {
|
||
|
buf[1] = 255
|
||
|
buf[2] = byte(length >> 24)
|
||
|
buf[3] = byte(length >> 16)
|
||
|
buf[4] = byte(length >> 8)
|
||
|
buf[5] = byte(length)
|
||
|
n = 6
|
||
|
}
|
||
|
|
||
|
_, err = w.Write(buf[:n])
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// serializeStreamHeader writes an OpenPGP packet header to w where the
|
||
|
// length of the packet is unknown. It returns a io.WriteCloser which can be
|
||
|
// used to write the contents of the packet. See RFC 4880, section 4.2.
|
||
|
func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) {
|
||
|
var buf [1]byte
|
||
|
buf[0] = 0x80 | 0x40 | byte(ptype)
|
||
|
_, err = w.Write(buf[:])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
out = &partialLengthWriter{w: w}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Packet represents an OpenPGP packet. Users are expected to try casting
|
||
|
// instances of this interface to specific packet types.
|
||
|
type Packet interface {
|
||
|
parse(io.Reader) error
|
||
|
}
|
||
|
|
||
|
// consumeAll reads from the given Reader until error, returning the number of
|
||
|
// bytes read.
|
||
|
func consumeAll(r io.Reader) (n int64, err error) {
|
||
|
var m int
|
||
|
var buf [1024]byte
|
||
|
|
||
|
for {
|
||
|
m, err = r.Read(buf[:])
|
||
|
n += int64(m)
|
||
|
if err == io.EOF {
|
||
|
err = nil
|
||
|
return
|
||
|
}
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// packetType represents the numeric ids of the different OpenPGP packet types. See
|
||
|
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2
|
||
|
type packetType uint8
|
||
|
|
||
|
const (
|
||
|
packetTypeEncryptedKey packetType = 1
|
||
|
packetTypeSignature packetType = 2
|
||
|
packetTypeSymmetricKeyEncrypted packetType = 3
|
||
|
packetTypeOnePassSignature packetType = 4
|
||
|
packetTypePrivateKey packetType = 5
|
||
|
packetTypePublicKey packetType = 6
|
||
|
packetTypePrivateSubkey packetType = 7
|
||
|
packetTypeCompressed packetType = 8
|
||
|
packetTypeSymmetricallyEncrypted packetType = 9
|
||
|
packetTypeLiteralData packetType = 11
|
||
|
packetTypeUserId packetType = 13
|
||
|
packetTypePublicSubkey packetType = 14
|
||
|
packetTypeUserAttribute packetType = 17
|
||
|
packetTypeSymmetricallyEncryptedMDC packetType = 18
|
||
|
)
|
||
|
|
||
|
// peekVersion detects the version of a public key packet about to
|
||
|
// be read. A bufio.Reader at the original position of the io.Reader
|
||
|
// is returned.
|
||
|
func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) {
|
||
|
bufr = bufio.NewReader(r)
|
||
|
var verBuf []byte
|
||
|
if verBuf, err = bufr.Peek(1); err != nil {
|
||
|
return
|
||
|
}
|
||
|
ver = verBuf[0]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Read reads a single OpenPGP packet from the given io.Reader. If there is an
|
||
|
// error parsing a packet, the whole packet is consumed from the input.
|
||
|
func Read(r io.Reader) (p Packet, err error) {
|
||
|
tag, _, contents, err := readHeader(r)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch tag {
|
||
|
case packetTypeEncryptedKey:
|
||
|
p = new(EncryptedKey)
|
||
|
case packetTypeSignature:
|
||
|
var version byte
|
||
|
// Detect signature version
|
||
|
if contents, version, err = peekVersion(contents); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if version < 4 {
|
||
|
p = new(SignatureV3)
|
||
|
} else {
|
||
|
p = new(Signature)
|
||
|
}
|
||
|
case packetTypeSymmetricKeyEncrypted:
|
||
|
p = new(SymmetricKeyEncrypted)
|
||
|
case packetTypeOnePassSignature:
|
||
|
p = new(OnePassSignature)
|
||
|
case packetTypePrivateKey, packetTypePrivateSubkey:
|
||
|
pk := new(PrivateKey)
|
||
|
if tag == packetTypePrivateSubkey {
|
||
|
pk.IsSubkey = true
|
||
|
}
|
||
|
p = pk
|
||
|
case packetTypePublicKey, packetTypePublicSubkey:
|
||
|
var version byte
|
||
|
if contents, version, err = peekVersion(contents); err != nil {
|
||
|
return
|
||
|
}
|
||
|
isSubkey := tag == packetTypePublicSubkey
|
||
|
if version < 4 {
|
||
|
p = &PublicKeyV3{IsSubkey: isSubkey}
|
||
|
} else {
|
||
|
p = &PublicKey{IsSubkey: isSubkey}
|
||
|
}
|
||
|
case packetTypeCompressed:
|
||
|
p = new(Compressed)
|
||
|
case packetTypeSymmetricallyEncrypted:
|
||
|
p = new(SymmetricallyEncrypted)
|
||
|
case packetTypeLiteralData:
|
||
|
p = new(LiteralData)
|
||
|
case packetTypeUserId:
|
||
|
p = new(UserId)
|
||
|
case packetTypeUserAttribute:
|
||
|
p = new(UserAttribute)
|
||
|
case packetTypeSymmetricallyEncryptedMDC:
|
||
|
se := new(SymmetricallyEncrypted)
|
||
|
se.MDC = true
|
||
|
p = se
|
||
|
default:
|
||
|
err = errors.UnknownPacketTypeError(tag)
|
||
|
}
|
||
|
if p != nil {
|
||
|
err = p.parse(contents)
|
||
|
}
|
||
|
if err != nil {
|
||
|
consumeAll(contents)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// SignatureType represents the different semantic meanings of an OpenPGP
|
||
|
// signature. See RFC 4880, section 5.2.1.
|
||
|
type SignatureType uint8
|
||
|
|
||
|
const (
|
||
|
SigTypeBinary SignatureType = 0
|
||
|
SigTypeText = 1
|
||
|
SigTypeGenericCert = 0x10
|
||
|
SigTypePersonaCert = 0x11
|
||
|
SigTypeCasualCert = 0x12
|
||
|
SigTypePositiveCert = 0x13
|
||
|
SigTypeSubkeyBinding = 0x18
|
||
|
SigTypePrimaryKeyBinding = 0x19
|
||
|
SigTypeDirectSignature = 0x1F
|
||
|
SigTypeKeyRevocation = 0x20
|
||
|
SigTypeSubkeyRevocation = 0x28
|
||
|
)
|
||
|
|
||
|
// PublicKeyAlgorithm represents the different public key system specified for
|
||
|
// OpenPGP. See
|
||
|
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12
|
||
|
type PublicKeyAlgorithm uint8
|
||
|
|
||
|
const (
|
||
|
PubKeyAlgoRSA PublicKeyAlgorithm = 1
|
||
|
PubKeyAlgoElGamal PublicKeyAlgorithm = 16
|
||
|
PubKeyAlgoDSA PublicKeyAlgorithm = 17
|
||
|
// RFC 6637, Section 5.
|
||
|
PubKeyAlgoECDH PublicKeyAlgorithm = 18
|
||
|
PubKeyAlgoECDSA PublicKeyAlgorithm = 19
|
||
|
|
||
|
// Deprecated in RFC 4880, Section 13.5. Use key flags instead.
|
||
|
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
|
||
|
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
|
||
|
)
|
||
|
|
||
|
// CanEncrypt returns true if it's possible to encrypt a message to a public
|
||
|
// key of the given type.
|
||
|
func (pka PublicKeyAlgorithm) CanEncrypt() bool {
|
||
|
switch pka {
|
||
|
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// CanSign returns true if it's possible for a public key of the given type to
|
||
|
// sign a message.
|
||
|
func (pka PublicKeyAlgorithm) CanSign() bool {
|
||
|
switch pka {
|
||
|
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// CipherFunction represents the different block ciphers specified for OpenPGP. See
|
||
|
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
|
||
|
type CipherFunction uint8
|
||
|
|
||
|
const (
|
||
|
Cipher3DES CipherFunction = 2
|
||
|
CipherCAST5 CipherFunction = 3
|
||
|
CipherAES128 CipherFunction = 7
|
||
|
CipherAES192 CipherFunction = 8
|
||
|
CipherAES256 CipherFunction = 9
|
||
|
)
|
||
|
|
||
|
// KeySize returns the key size, in bytes, of cipher.
|
||
|
func (cipher CipherFunction) KeySize() int {
|
||
|
switch cipher {
|
||
|
case Cipher3DES:
|
||
|
return 24
|
||
|
case CipherCAST5:
|
||
|
return cast5.KeySize
|
||
|
case CipherAES128:
|
||
|
return 16
|
||
|
case CipherAES192:
|
||
|
return 24
|
||
|
case CipherAES256:
|
||
|
return 32
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// blockSize returns the block size, in bytes, of cipher.
|
||
|
func (cipher CipherFunction) blockSize() int {
|
||
|
switch cipher {
|
||
|
case Cipher3DES:
|
||
|
return des.BlockSize
|
||
|
case CipherCAST5:
|
||
|
return 8
|
||
|
case CipherAES128, CipherAES192, CipherAES256:
|
||
|
return 16
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// new returns a fresh instance of the given cipher.
|
||
|
func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
|
||
|
switch cipher {
|
||
|
case Cipher3DES:
|
||
|
block, _ = des.NewTripleDESCipher(key)
|
||
|
case CipherCAST5:
|
||
|
block, _ = cast5.NewCipher(key)
|
||
|
case CipherAES128, CipherAES192, CipherAES256:
|
||
|
block, _ = aes.NewCipher(key)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// readMPI reads a big integer from r. The bit length returned is the bit
|
||
|
// length that was specified in r. This is preserved so that the integer can be
|
||
|
// reserialized exactly.
|
||
|
func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
|
||
|
var buf [2]byte
|
||
|
_, err = readFull(r, buf[0:])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
bitLength = uint16(buf[0])<<8 | uint16(buf[1])
|
||
|
numBytes := (int(bitLength) + 7) / 8
|
||
|
mpi = make([]byte, numBytes)
|
||
|
_, err = readFull(r, mpi)
|
||
|
// According to RFC 4880 3.2. we should check that the MPI has no leading
|
||
|
// zeroes (at least when not an encrypted MPI?), but this implementation
|
||
|
// does generate leading zeroes, so we keep accepting them.
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// writeMPI serializes a big integer to w.
|
||
|
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
|
||
|
// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
|
||
|
// Implementations seem to be tolerant of them, and stripping them would
|
||
|
// make it complex to guarantee matching re-serialization.
|
||
|
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
|
||
|
if err == nil {
|
||
|
_, err = w.Write(mpiBytes)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// writeBig serializes a *big.Int to w.
|
||
|
func writeBig(w io.Writer, i *big.Int) error {
|
||
|
return writeMPI(w, uint16(i.BitLen()), i.Bytes())
|
||
|
}
|
||
|
|
||
|
// padToKeySize left-pads a MPI with zeroes to match the length of the
|
||
|
// specified RSA public.
|
||
|
func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
|
||
|
k := (pub.N.BitLen() + 7) / 8
|
||
|
if len(b) >= k {
|
||
|
return b
|
||
|
}
|
||
|
bb := make([]byte, k)
|
||
|
copy(bb[len(bb)-len(b):], b)
|
||
|
return bb
|
||
|
}
|
||
|
|
||
|
// CompressionAlgo Represents the different compression algorithms
|
||
|
// supported by OpenPGP (except for BZIP2, which is not currently
|
||
|
// supported). See Section 9.3 of RFC 4880.
|
||
|
type CompressionAlgo uint8
|
||
|
|
||
|
const (
|
||
|
CompressionNone CompressionAlgo = 0
|
||
|
CompressionZIP CompressionAlgo = 1
|
||
|
CompressionZLIB CompressionAlgo = 2
|
||
|
)
|