UNCLASSIFIED

Commit 530cd297 authored by sean.melissari's avatar sean.melissari
Browse files

v0.13.0

parent baf57376
Pipeline #281750 passed with stages
in 10 minutes and 31 seconds
// 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 openpgp
import (
"hash"
"io"
)
// NewCanonicalTextHash reformats text written to it into the canonical
// form and then applies the hash h. See RFC 4880, section 5.2.1.
func NewCanonicalTextHash(h hash.Hash) hash.Hash {
return &canonicalTextHash{h, 0}
}
type canonicalTextHash struct {
h hash.Hash
s int
}
var newline = []byte{'\r', '\n'}
func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) {
start := 0
for i, c := range buf {
switch *s {
case 0:
if c == '\r' {
*s = 1
} else if c == '\n' {
cw.Write(buf[start:i])
cw.Write(newline)
start = i + 1
}
case 1:
*s = 0
}
}
cw.Write(buf[start:])
return len(buf), nil
}
func (cth *canonicalTextHash) Write(buf []byte) (int, error) {
return writeCanonical(cth.h, buf, &cth.s)
}
func (cth *canonicalTextHash) Sum(in []byte) []byte {
return cth.h.Sum(in)
}
func (cth *canonicalTextHash) Reset() {
cth.h.Reset()
cth.s = 0
}
func (cth *canonicalTextHash) Size() int {
return cth.h.Size()
}
func (cth *canonicalTextHash) BlockSize() int {
return cth.h.BlockSize()
}
// Copyright 2017 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 ecdh implements ECDH encryption, suitable for OpenPGP,
// as specified in RFC 6637, section 8.
package ecdh
import (
"bytes"
"crypto/elliptic"
"errors"
"io"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
)
type KDF struct {
Hash algorithm.Hash
Cipher algorithm.Cipher
}
type PublicKey struct {
ecc.CurveType
elliptic.Curve
X, Y *big.Int
KDF
}
type PrivateKey struct {
PublicKey
D []byte
}
func GenerateKey(c elliptic.Curve, kdf KDF, rand io.Reader) (priv *PrivateKey, err error) {
priv = new(PrivateKey)
priv.PublicKey.Curve = c
priv.PublicKey.KDF = kdf
priv.D, priv.PublicKey.X, priv.PublicKey.Y, err = elliptic.GenerateKey(c, rand)
return
}
func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) {
if len(msg) > 40 {
return nil, nil, errors.New("ecdh: message too long")
}
// the sender MAY use 21, 13, and 5 bytes of padding for AES-128,
// AES-192, and AES-256, respectively, to provide the same number of
// octets, 40 total, as an input to the key wrapping method.
padding := make([]byte, 40-len(msg))
for i := range padding {
padding[i] = byte(40 - len(msg))
}
m := append(msg, padding...)
if pub.CurveType == ecc.Curve25519 {
return X25519Encrypt(random, pub, m, curveOID, fingerprint)
}
d, x, y, err := elliptic.GenerateKey(pub.Curve, random)
if err != nil {
return nil, nil, err
}
vsG = elliptic.Marshal(pub.Curve, x, y)
zbBig, _ := pub.Curve.ScalarMult(pub.X, pub.Y, d)
byteLen := (pub.Curve.Params().BitSize + 7) >> 3
zb := make([]byte, byteLen)
zbBytes := zbBig.Bytes()
copy(zb[byteLen-len(zbBytes):], zbBytes)
z, err := buildKey(pub, zb, curveOID, fingerprint, false, false)
if err != nil {
return nil, nil, err
}
if c, err = keywrap.Wrap(z, m); err != nil {
return nil, nil, err
}
return vsG, c, nil
}
func Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) {
if priv.PublicKey.CurveType == ecc.Curve25519 {
return X25519Decrypt(priv, vsG, m, curveOID, fingerprint)
}
x, y := elliptic.Unmarshal(priv.Curve, vsG)
zbBig, _ := priv.Curve.ScalarMult(x, y, priv.D)
byteLen := (priv.Curve.Params().BitSize + 7) >> 3
zb := make([]byte, byteLen)
zbBytes := zbBig.Bytes()
copy(zb[byteLen-len(zbBytes):], zbBytes)
z, err := buildKey(&priv.PublicKey, zb, curveOID, fingerprint, false, false)
if err != nil {
return nil, err
}
c, err := keywrap.Unwrap(z, m)
if err != nil {
return nil, err
}
return c[:len(c)-int(c[len(c)-1])], nil
}
func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLeading, stripTrailing bool) ([]byte, error) {
// Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
// || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap
// || "Anonymous Sender " || recipient_fingerprint;
param := new(bytes.Buffer)
if _, err := param.Write(curveOID); err != nil {
return nil, err
}
algKDF := []byte{18, 3, 1, pub.KDF.Hash.Id(), pub.KDF.Cipher.Id()}
if _, err := param.Write(algKDF); err != nil {
return nil, err
}
if _, err := param.Write([]byte("Anonymous Sender ")); err != nil {
return nil, err
}
// For v5 keys, the 20 leftmost octets of the fingerprint are used.
if _, err := param.Write(fingerprint[:20]); err != nil {
return nil, err
}
if param.Len() - len(curveOID) != 45 {
return nil, errors.New("ecdh: malformed KDF Param")
}
// MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
h := pub.KDF.Hash.New()
if _, err := h.Write([]byte{0x0, 0x0, 0x0, 0x1}); err != nil {
return nil, err
}
zbLen := len(zb)
i := 0
j := zbLen - 1
if stripLeading {
// Work around old go crypto bug where the leading zeros are missing.
for ; i < zbLen && zb[i] == 0; i++ {}
}
if stripTrailing {
// Work around old OpenPGP.js bug where insignificant trailing zeros in
// this little-endian number are missing.
// (See https://github.com/openpgpjs/openpgpjs/pull/853.)
for ; j >= 0 && zb[j] == 0; j-- {}
}
if _, err := h.Write(zb[i:j+1]); err != nil {
return nil, err
}
if _, err := h.Write(param.Bytes()); err != nil {
return nil, err
}
mb := h.Sum(nil)
return mb[:pub.KDF.Cipher.KeySize()], nil // return oBits leftmost bits of MB.
}
// Copyright 2017 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 ecdh implements ECDH encryption, suitable for OpenPGP,
// as specified in RFC 6637, section 8.
package ecdh
import (
"errors"
"io"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"golang.org/x/crypto/curve25519"
)
// Generates a private-public key-pair.
// 'priv' is a private key; a scalar belonging to the set
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of the
// curve. 'pub' is simply 'priv' * G where G is the base point.
// See https://cr.yp.to/ecdh.html and RFC7748, sec 5.
func x25519GenerateKeyPairBytes(rand io.Reader) (priv [32]byte, pub [32]byte, err error) {
var n, helper = new(big.Int), new(big.Int)
n.SetUint64(1)
n.Lsh(n, 252)
helper.SetString("27742317777372353535851937790883648493", 10)
n.Add(n, helper)
for true {
_, err = io.ReadFull(rand, priv[:])
if err != nil {
return
}
// The following ensures that the private key is a number of the form
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of
// of the curve.
priv[0] &= 248
priv[31] &= 127
priv[31] |= 64
// If the scalar is out of range, sample another random number.
if new(big.Int).SetBytes(priv[:]).Cmp(n) >= 0 {
continue
}
curve25519.ScalarBaseMult(&pub, &priv)
return
}
return
}
// X25519GenerateKey samples the key pair according to the correct distribution.
// It also sets the given key-derivation function and returns the *PrivateKey
// object along with an error.
func X25519GenerateKey(rand io.Reader, kdf KDF) (priv *PrivateKey, err error) {
ci := ecc.FindByName("Curve25519")
priv = new(PrivateKey)
priv.PublicKey.Curve = ci.Curve
d, pubKey, err := x25519GenerateKeyPairBytes(rand)
if err != nil {
return nil, err
}
priv.PublicKey.KDF = kdf
priv.D = make([]byte, 32)
copyReversed(priv.D, d[:])
priv.PublicKey.CurveType = ci.CurveType
priv.PublicKey.Curve = ci.Curve
/*
* Note that ECPoint.point differs from the definition of public keys in
* [Curve25519] in two ways: (1) the byte-ordering is big-endian, which is
* more uniform with how big integers are represented in TLS, and (2) there
* is an additional length byte (so ECpoint.point is actually 33 bytes),
* again for uniformity (and extensibility).
*/
var encodedKey = make([]byte, 33)
encodedKey[0] = 0x40
copy(encodedKey[1:], pubKey[:])
priv.PublicKey.X = new(big.Int).SetBytes(encodedKey[:])
priv.PublicKey.Y = new(big.Int)
return priv, nil
}
func X25519Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) {
d, ephemeralKey, err := x25519GenerateKeyPairBytes(random)
if err != nil {
return nil, nil, err
}
var pubKey [32]byte
if pub.X.BitLen() > 33*264 {
return nil, nil, errors.New("ecdh: invalid key")
}
copy(pubKey[:], pub.X.Bytes()[1:])
var zb [32]byte
curve25519.ScalarBaseMult(&zb, &d)
curve25519.ScalarMult(&zb, &d, &pubKey)
z, err := buildKey(pub, zb[:], curveOID, fingerprint, false, false)
if err != nil {
return nil, nil, err
}
if c, err = keywrap.Wrap(z, msg); err != nil {
return nil, nil, err
}
var vsg [33]byte
vsg[0] = 0x40
copy(vsg[1:], ephemeralKey[:])
return vsg[:], c, nil
}
func X25519Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) {
var zb, d, ephemeralKey [32]byte
if len(vsG) != 33 || vsG[0] != 0x40 {
return nil, errors.New("ecdh: invalid key")
}
copy(ephemeralKey[:], vsG[1:33])
copyReversed(d[:], priv.D)
curve25519.ScalarBaseMult(&zb, &d)
curve25519.ScalarMult(&zb, &d, &ephemeralKey)
var c []byte
for i := 0; i < 3; i++ {
// Try buildKey three times for compat, see comments in buildKey.
z, err := buildKey(&priv.PublicKey, zb[:], curveOID, fingerprint, i == 1, i == 2)
if err != nil {
return nil, err
}
res, err := keywrap.Unwrap(z, m)
if i == 2 && err != nil {
// Only return an error after we've tried all variants of buildKey.
return nil, err
}
c = res
if err == nil {
break
}
}
return c[:len(c)-int(c[len(c)-1])], nil
}
func copyReversed(out []byte, in []byte) {
l := len(in)
for i := 0; i < l; i++ {
out[i] = in[l-i-1]
}
}
// 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 elgamal implements ElGamal encryption, suitable for OpenPGP,
// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on
// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31,
// n. 4, 1985, pp. 469-472.
//
// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
// unsuitable for other protocols. RSA should be used in preference in any
// case.
package elgamal // import "github.com/ProtonMail/go-crypto/openpgp/elgamal"
import (
"crypto/rand"
"crypto/subtle"
"errors"
"io"
"math/big"
)
// PublicKey represents an ElGamal public key.
type PublicKey struct {
G, P, Y *big.Int
}
// PrivateKey represents an ElGamal private key.
type PrivateKey struct {
PublicKey
X *big.Int
}
// Encrypt encrypts the given message to the given public key. The result is a
// pair of integers. Errors can result from reading random, or because msg is
// too large to be encrypted to the public key.
func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) {
pLen := (pub.P.BitLen() + 7) / 8
if len(msg) > pLen-11 {
err = errors.New("elgamal: message too long")
return
}
// EM = 0x02 || PS || 0x00 || M
em := make([]byte, pLen-1)
em[0] = 2
ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
err = nonZeroRandomBytes(ps, random)
if err != nil {
return
}
em[len(em)-len(msg)-1] = 0
copy(mm, msg)
m := new(big.Int).SetBytes(em)
k, err := rand.Int(random, pub.P)
if err != nil {
return
}
c1 = new(big.Int).Exp(pub.G, k, pub.P)
s := new(big.Int).Exp(pub.Y, k, pub.P)
c2 = s.Mul(s, m)
c2.Mod(c2, pub.P)
return
}
// Decrypt takes two integers, resulting from an ElGamal encryption, and
// returns the plaintext of the message. An error can result only if the
// ciphertext is invalid. Users should keep in mind that this is a padding
// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks
// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel
// Bleichenbacher, Advances in Cryptology (Crypto '98),
func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
s := new(big.Int).Exp(c1, priv.X, priv.P)
if s.ModInverse(s, priv.P) == nil {
return nil, errors.New("elgamal: invalid private key")
}
s.Mul(s, c2)
s.Mod(s, priv.P)
em := s.Bytes()
firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2)
// The remainder of the plaintext must be a string of non-zero random
// octets, followed by a 0, followed by the message.
// lookingForIndex: 1 iff we are still looking for the zero.
// index: the offset of the first zero byte.
var lookingForIndex, index int
lookingForIndex = 1
for i := 1; i < len(em); i++ {
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
}
if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 {
return nil, errors.New("elgamal: decryption error")
}
return em[index+1:], nil
}
// nonZeroRandomBytes fills the given slice with non-zero random octets.
func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
_, err = io.ReadFull(rand, s)
if err != nil {
return
}
for i := 0; i < len(s); i++ {
for s[i] == 0 {
_, err = io.ReadFull(rand, s[i:i+1])
if err != nil {
return
}
}
}
return
}
// Copyright 2010 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 errors contains common error types for the OpenPGP packages.
package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors"
import (
"strconv"
)
// A StructuralError is returned when OpenPGP data is found to be syntactically
// invalid.
type StructuralError string
func (s StructuralError) Error() string {
return "openpgp: invalid data: " + string(s)
}
// UnsupportedError indicates that, although the OpenPGP data is valid, it
// makes use of currently unimplemented features.
type UnsupportedError string
func (s UnsupportedError) Error() string {
return "openpgp: unsupported feature: " + string(s)
}
// InvalidArgumentError indicates that the caller is in error and passed an
// incorrect value.
type InvalidArgumentError string
func (i InvalidArgumentError) Error() string {
return "openpgp: invalid argument: " + string(i)
}
// SignatureError indicates that a syntactically valid signature failed to
// validate.
type SignatureError string
func (b SignatureError) Error() string {
return "openpgp: invalid signature: " + string(b)
}
var ErrMDCHashMismatch error = SignatureError("MDC hash mismatch")
var ErrMDCMissing error = SignatureError("MDC packet not found")
type signatureExpiredError int
func (se signatureExpiredError) Error() string {
return "openpgp: signature expired"
}
var ErrSignatureExpired error = signatureExpiredError(0)
type keyExpiredError int
func (ke keyExpiredError) Error() string {
return "openpgp: key expired"
}
var ErrKeyExpired error = keyExpiredError(0)
type keyIncorrectError int
func (ki keyIncorrectError) Error() string {
return "openpgp: incorrect key"
}
var ErrKeyIncorrect error = keyIncorrectError(0)
// KeyInvalidError indicates that the public key parameters are invalid
// as they do not match the private ones
type KeyInvalidError string
func (e KeyInvalidError) Error() string {
return "openpgp: invalid key: " + string(e)
}
type unknownIssuerError int
func (unknownIssuerError) Error() string {
return "openpgp: signature made by unknown entity"
}
var ErrUnknownIssuer error = unknownIssuerError(0)
type keyRevokedError int
func (keyRevokedError) Error() string {
return "openpgp: signature made by revoked key"
}
var ErrKeyRevoked error = keyRevokedError(0)
type UnknownPacketTypeError uint8
func (upte UnknownPacketTypeError) Error() string {
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
}
// AEADError indicates that there is a problem when initializing or using a
// AEAD instance, configuration struct, nonces or index values.
type AEADError string
func (ae AEADError) Error() string {
return "openpgp: aead error: " + string(ae)
}
// ErrDummyPrivateKey results when operations are attempted on a private key
// that is just a dummy key. See
// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109
type ErrDummyPrivateKey string
func (dke ErrDummyPrivateKey) Error() string {
return "openpgp: s2k GNU dummy key: " + string(dke)
}
// Copyright (C) 2019 ProtonTech AG
package algorithm
import (
"crypto/cipher"
"github.com/ProtonMail/go-crypto/eax"
"github.com/ProtonMail/go-crypto/ocb"
)
// AEADMode defines the Authenticated Encryption with Associated Data mode of
// operation.
type AEADMode uint8
// Supported modes of operation (see RFC4880bis [EAX] and RFC7253)
const (
AEADModeEAX = AEADMode(1)
AEADModeOCB = AEADMode(2)
AEADModeGCM = AEADMode(100)
)
// TagLength returns the length in bytes of authentication tags.
func (mode AEADMode) TagLength() int {
switch mode {
case AEADModeEAX:
return 16
case AEADModeOCB:
return 16
case AEADModeGCM:
return 16
default:
return 0
}
}
// NonceLength returns the length in bytes of nonces.
func (mode AEADMode) NonceLength() int {
switch mode {
case AEADModeEAX:
return 16
case AEADModeOCB:
return 15
case AEADModeGCM:
return 12
default:
return 0
}
}
// New returns a fresh instance of the given mode
func (mode AEADMode) New(block cipher.Block) (alg cipher.AEAD) {
var err error
switch mode {
case AEADModeEAX:
alg, err = eax.NewEAX(block)
case AEADModeOCB:
alg, err = ocb.NewOCB(block)
case AEADModeGCM:
alg, err = cipher.NewGCM(block)
}
if err != nil {
panic(err.Error())
}
return alg
}
// Copyright 2017 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 algorithm
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"golang.org/x/crypto/cast5"
)
// Cipher is an official symmetric key cipher algorithm. See RFC 4880,
// section 9.2.
type Cipher interface {
// Id returns the algorithm ID, as a byte, of the cipher.
Id() uint8
// KeySize returns the key size, in bytes, of the cipher.
KeySize() int
// BlockSize returns the block size, in bytes, of the cipher.
BlockSize() int
// New returns a fresh instance of the given cipher.
New(key []byte) cipher.Block
}
// The following constants mirror the OpenPGP standard (RFC 4880).
const (
TripleDES = CipherFunction(2)
CAST5 = CipherFunction(3)
AES128 = CipherFunction(7)
AES192 = CipherFunction(8)
AES256 = CipherFunction(9)
)
// CipherById represents the different block ciphers specified for OpenPGP. See
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
var CipherById = map[uint8]Cipher{
TripleDES.Id(): TripleDES,
CAST5.Id(): CAST5,
AES128.Id(): AES128,
AES192.Id(): AES192,
AES256.Id(): AES256,
}
type CipherFunction uint8
// ID returns the algorithm Id, as a byte, of cipher.
func (sk CipherFunction) Id() uint8 {
return uint8(sk)
}
var keySizeByID = map[uint8]int{
TripleDES.Id(): 24,
CAST5.Id(): cast5.KeySize,
AES128.Id(): 16,
AES192.Id(): 24,
AES256.Id(): 32,
}
// KeySize returns the key size, in bytes, of cipher.
func (cipher CipherFunction) KeySize() int {
switch cipher {
case TripleDES:
return 24
case CAST5:
return cast5.KeySize
case AES128:
return 16
case AES192:
return 24
case AES256:
return 32
}
return 0
}
// BlockSize returns the block size, in bytes, of cipher.
func (cipher CipherFunction) BlockSize() int {
switch cipher {
case TripleDES:
return des.BlockSize
case CAST5:
return 8
case AES128, AES192, AES256:
return 16
}
return 0
}
// New returns a fresh instance of the given cipher.
func (cipher CipherFunction) New(key []byte) (block cipher.Block) {
var err error
switch cipher {
case TripleDES:
block, err = des.NewTripleDESCipher(key)
case CAST5:
block, err = cast5.NewCipher(key)
case AES128, AES192, AES256:
block, err = aes.NewCipher(key)
}
if err != nil {
panic(err.Error())
}
return
}
// Copyright 2017 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 algorithm
import (
"crypto"
"fmt"
"hash"
)
// Hash is an official hash function algorithm. See RFC 4880, section 9.4.
type Hash interface {
// Id returns the algorithm ID, as a byte, of Hash.
Id() uint8
// Available reports whether the given hash function is linked into the binary.
Available() bool
// HashFunc simply returns the value of h so that Hash implements SignerOpts.
HashFunc() crypto.Hash
// New returns a new hash.Hash calculating the given hash function. New
// panics if the hash function is not linked into the binary.
New() hash.Hash
// Size returns the length, in bytes, of a digest resulting from the given
// hash function. It doesn't require that the hash function in question be
// linked into the program.
Size() int
// String is the name of the hash function corresponding to the given
// OpenPGP hash id.
String() string
}
// The following vars mirror the crypto/Hash supported hash functions.
var (
MD5 Hash = cryptoHash{1, crypto.MD5}
SHA1 Hash = cryptoHash{2, crypto.SHA1}
RIPEMD160 Hash = cryptoHash{3, crypto.RIPEMD160}
SHA256 Hash = cryptoHash{8, crypto.SHA256}
SHA384 Hash = cryptoHash{9, crypto.SHA384}
SHA512 Hash = cryptoHash{10, crypto.SHA512}
SHA224 Hash = cryptoHash{11, crypto.SHA224}
)
// HashById represents the different hash functions specified for OpenPGP. See
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14
var (
HashById = map[uint8]Hash{
MD5.Id(): MD5,
SHA1.Id(): SHA1,
RIPEMD160.Id(): RIPEMD160,
SHA256.Id(): SHA256,
SHA384.Id(): SHA384,
SHA512.Id(): SHA512,
SHA224.Id(): SHA224,
}
)
// cryptoHash contains pairs relating OpenPGP's hash identifier with
// Go's crypto.Hash type. See RFC 4880, section 9.4.
type cryptoHash struct {
id uint8
crypto.Hash
}
// Id returns the algorithm ID, as a byte, of cryptoHash.
func (h cryptoHash) Id() uint8 {
return h.id
}
var hashNames = map[uint8]string{
MD5.Id(): "MD5",
SHA1.Id(): "SHA1",
RIPEMD160.Id(): "RIPEMD160",
SHA256.Id(): "SHA256",
SHA384.Id(): "SHA384",
SHA512.Id(): "SHA512",
SHA224.Id(): "SHA224",
}
func (h cryptoHash) String() string {
s, ok := hashNames[h.id]
if !ok {
panic(fmt.Sprintf("Unsupported hash function %d", h.id))
}
return s
}
package ecc
import (
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
"crypto/elliptic"
"bytes"
"github.com/ProtonMail/go-crypto/bitcurves"
"github.com/ProtonMail/go-crypto/brainpool"
)
type SignatureAlgorithm uint8
const (
ECDSA SignatureAlgorithm = 1
EdDSA SignatureAlgorithm = 2
)
type CurveInfo struct {
Name string
Oid *encoding.OID
Curve elliptic.Curve
SigAlgorithm SignatureAlgorithm
CurveType CurveType
}
var curves = []CurveInfo{
{
Name: "NIST curve P-256",
Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}),
Curve: elliptic.P256(),
CurveType: NISTCurve,
SigAlgorithm: ECDSA,
},
{
Name: "NIST curve P-384",
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}),
Curve: elliptic.P384(),
CurveType: NISTCurve,
SigAlgorithm: ECDSA,
},
{
Name: "NIST curve P-521",
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}),
Curve: elliptic.P521(),
CurveType: NISTCurve,
SigAlgorithm: ECDSA,
},
{
Name: "SecP256k1",
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}),
Curve: bitcurves.S256(),
CurveType: BitCurve,
SigAlgorithm: ECDSA,
},
{
Name: "Curve25519",
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}),
Curve: elliptic.P256(),// filler
CurveType: Curve25519,
SigAlgorithm: ECDSA,
},
{
Name: "Ed25519",
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}),
Curve: elliptic.P256(), // filler
CurveType: NISTCurve,
SigAlgorithm: EdDSA,
},
{
Name: "Brainpool P256r1",
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}),
Curve: brainpool.P256r1(),
CurveType: BrainpoolCurve,
SigAlgorithm: ECDSA,
},
{
Name: "BrainpoolP384r1",
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}),
Curve: brainpool.P384r1(),
CurveType: BrainpoolCurve,
SigAlgorithm: ECDSA,
},
{
Name: "BrainpoolP512r1",
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}),
Curve: brainpool.P512r1(),
CurveType: BrainpoolCurve,
SigAlgorithm: ECDSA,
},
}
func FindByCurve(curve elliptic.Curve) *CurveInfo {
for _, curveInfo := range curves {
if curveInfo.Curve == curve {
return &curveInfo
}
}
return nil
}
func FindByOid(oid encoding.Field) *CurveInfo {
var rawBytes = oid.Bytes()
for _, curveInfo := range curves {
if bytes.Equal(curveInfo.Oid.Bytes(), rawBytes) {
return &curveInfo
}
}
return nil
}
func FindByName(name string) *CurveInfo {
for _, curveInfo := range curves {
if curveInfo.Name == name {
return &curveInfo
}
}
return nil
}
\ No newline at end of file
package ecc
type CurveType uint8
const (
NISTCurve CurveType = 1
Curve25519 CurveType = 2
BitCurve CurveType = 3
BrainpoolCurve CurveType = 4
)
\ No newline at end of file
// Copyright 2017 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 encoding implements openpgp packet field encodings as specified in
// RFC 4880 and 6637.
package encoding
import "io"
// Field is an encoded field of an openpgp packet.
type Field interface {
// Bytes returns the decoded data.
Bytes() []byte
// BitLength is the size in bits of the decoded data.
BitLength() uint16
// EncodedBytes returns the encoded data.
EncodedBytes() []byte
// EncodedLength is the size in bytes of the encoded data.
EncodedLength() uint16
// ReadFrom reads the next Field from r.
ReadFrom(r io.Reader) (int64, error)
}
// Copyright 2017 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 encoding
import (
"io"
"math/big"
"math/bits"
)
// An MPI is used to store the contents of a big integer, along with the bit
// length that was specified in the original input. This allows the MPI to be
// reserialized exactly.
type MPI struct {
bytes []byte
bitLength uint16
}
// NewMPI returns a MPI initialized with bytes.
func NewMPI(bytes []byte) *MPI {
for len(bytes) != 0 && bytes[0] == 0 {
bytes = bytes[1:]
}
if len(bytes) == 0 {
bitLength := uint16(0)
return &MPI{bytes, bitLength}
}
bitLength := 8*uint16(len(bytes)-1) + uint16(bits.Len8(bytes[0]))
return &MPI{bytes, bitLength}
}
// Bytes returns the decoded data.
func (m *MPI) Bytes() []byte {
return m.bytes
}
// BitLength is the size in bits of the decoded data.
func (m *MPI) BitLength() uint16 {
return m.bitLength
}
// EncodedBytes returns the encoded data.
func (m *MPI) EncodedBytes() []byte {
return append([]byte{byte(m.bitLength >> 8), byte(m.bitLength)}, m.bytes...)
}
// EncodedLength is the size in bytes of the encoded data.
func (m *MPI) EncodedLength() uint16 {
return uint16(2 + len(m.bytes))
}
// ReadFrom reads into m the next MPI from r.
func (m *MPI) ReadFrom(r io.Reader) (int64, error) {
var buf [2]byte
n, err := io.ReadFull(r, buf[0:])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return int64(n), err
}
m.bitLength = uint16(buf[0])<<8 | uint16(buf[1])
m.bytes = make([]byte, (int(m.bitLength)+7)/8)
nn, err := io.ReadFull(r, m.bytes)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
// remove leading zero bytes from malformed GnuPG encoded MPIs:
// https://bugs.gnupg.org/gnupg/issue1853
// for _, b := range m.bytes {
// if b != 0 {
// break
// }
// m.bytes = m.bytes[1:]
// m.bitLength -= 8
// }
return int64(n) + int64(nn), err
}
// SetBig initializes m with the bits from n.
func (m *MPI) SetBig(n *big.Int) *MPI {
m.bytes = n.Bytes()
m.bitLength = uint16(n.BitLen())
return m
}
// Copyright 2017 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 encoding
import (
"io"
"github.com/ProtonMail/go-crypto/openpgp/errors"
)
// OID is used to store a variable-length field with a one-octet size
// prefix. See https://tools.ietf.org/html/rfc6637#section-9.
type OID struct {
bytes []byte
}
const (
// maxOID is the maximum number of bytes in a OID.
maxOID = 254
// reservedOIDLength1 and reservedOIDLength2 are OID lengths that the RFC
// specifies are reserved.
reservedOIDLength1 = 0
reservedOIDLength2 = 0xff
)
// NewOID returns a OID initialized with bytes.
func NewOID(bytes []byte) *OID {
switch len(bytes) {
case reservedOIDLength1, reservedOIDLength2:
panic("encoding: NewOID argument length is reserved")
default:
if len(bytes) > maxOID {
panic("encoding: NewOID argment too large")
}
}
return &OID{
bytes: bytes,
}
}
// Bytes returns the decoded data.
func (o *OID) Bytes() []byte {
return o.bytes
}
// BitLength is the size in bits of the decoded data.
func (o *OID) BitLength() uint16 {
return uint16(len(o.bytes) * 8)
}
// EncodedBytes returns the encoded data.
func (o *OID) EncodedBytes() []byte {
return append([]byte{byte(len(o.bytes))}, o.bytes...)
}
// EncodedLength is the size in bytes of the encoded data.
func (o *OID) EncodedLength() uint16 {
return uint16(1 + len(o.bytes))
}
// ReadFrom reads into b the next OID from r.
func (o *OID) ReadFrom(r io.Reader) (int64, error) {
var buf [1]byte
n, err := io.ReadFull(r, buf[:])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return int64(n), err
}
switch buf[0] {
case reservedOIDLength1, reservedOIDLength2:
return int64(n), errors.UnsupportedError("reserved for future extensions")
}
o.bytes = make([]byte, buf[0])
nn, err := io.ReadFull(r, o.bytes)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return int64(n) + int64(nn), err
}
// 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 openpgp
import (
"crypto"
"crypto/rand"
"crypto/rsa"
goerrors "errors"
"io"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"golang.org/x/crypto/ed25519"
)
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
// single identity composed of the given full name, comment and email, any of
// which may be empty but must not contain any of "()<>\x00".
// If config is nil, sensible defaults will be used.
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
uid := packet.NewUserId(name, comment, email)
if uid == nil {
return nil, errors.InvalidArgumentError("user id field contained invalid characters")
}
// Generate a primary signing key
primaryPrivRaw, err := newSigner(config)
if err != nil {
return nil, err
}
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
if config != nil && config.V5Keys {
primary.UpgradeToV5()
}
isPrimaryId := true
selfSignature := &packet.Signature{
Version: primary.PublicKey.Version,
SigType: packet.SigTypePositiveCert,
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
Hash: config.Hash(),
CreationTime: creationTime,
KeyLifetimeSecs: &keyLifetimeSecs,
IssuerKeyId: &primary.PublicKey.KeyId,
IssuerFingerprint: primary.PublicKey.Fingerprint,
IsPrimaryId: &isPrimaryId,
FlagsValid: true,
FlagSign: true,
FlagCertify: true,
MDC: true, // true by default, see 5.8 vs. 5.14
AEAD: config.AEAD() != nil,
V5Keys: config != nil && config.V5Keys,
}
// Set the PreferredHash for the SelfSignature from the packet.Config.
// If it is not the must-implement algorithm from rfc4880bis, append that.
selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())}
if config.Hash() != crypto.SHA256 {
selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256))
}
// Likewise for DefaultCipher.
selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())}
if config.Cipher() != packet.CipherAES128 {
selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128))
}
// And for DefaultMode.
selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())}
if config.AEAD().Mode() != packet.AEADModeEAX {
selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX))
}
// User ID binding signature
err = selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config)
if err != nil {
return nil, err
}
// Generate an encryption subkey
subPrivRaw, err := newDecrypter(config)
if err != nil {
return nil, err
}
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
sub.IsSubkey = true
sub.PublicKey.IsSubkey = true
if config != nil && config.V5Keys {
sub.UpgradeToV5()
}
// NOTE: No KeyLifetimeSecs here, but we will not return this subkey in EncryptionKey()
// if the primary/master key has expired.
subKey := Subkey{
PublicKey: &sub.PublicKey,
PrivateKey: sub,
Sig: &packet.Signature{
Version: primary.PublicKey.Version,
CreationTime: creationTime,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
Hash: config.Hash(),
FlagsValid: true,
FlagEncryptStorage: true,
FlagEncryptCommunications: true,
IssuerKeyId: &primary.PublicKey.KeyId,
},
}
// Subkey binding signature
err = subKey.Sig.SignKey(subKey.PublicKey, primary, config)
if err != nil {
return nil, err
}
return &Entity{
PrimaryKey: &primary.PublicKey,
PrivateKey: primary,
Identities: map[string]*Identity{
uid.Id: &Identity{
Name: uid.Id,
UserId: uid,
SelfSignature: selfSignature,
Signatures: []*packet.Signature{selfSignature},
},
},
Subkeys: []Subkey{subKey},
}, nil
}
// AddSigningSubkey adds a signing keypair as a subkey to the Entity.
// If config is nil, sensible defaults will be used.
func (e *Entity) AddSigningSubkey(config *packet.Config) error {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
subPrivRaw, err := newSigner(config)
if err != nil {
return err
}
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw)
subkey := Subkey{
PublicKey: &sub.PublicKey,
PrivateKey: sub,
Sig: &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: creationTime,
KeyLifetimeSecs: &keyLifetimeSecs,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
Hash: config.Hash(),
FlagsValid: true,
FlagSign: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
EmbeddedSignature: &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: creationTime,
SigType: packet.SigTypePrimaryKeyBinding,
PubKeyAlgo: sub.PublicKey.PubKeyAlgo,
Hash: config.Hash(),
IssuerKeyId: &e.PrimaryKey.KeyId,
},
},
}
if config != nil && config.V5Keys {
subkey.PublicKey.UpgradeToV5()
}
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config)
if err != nil {
return err
}
subkey.PublicKey.IsSubkey = true
subkey.PrivateKey.IsSubkey = true
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
return err
}
e.Subkeys = append(e.Subkeys, subkey)
return nil
}
// AddEncryptionSubkey adds an encryption keypair as a subkey to the Entity.
// If config is nil, sensible defaults will be used.
func (e *Entity) AddEncryptionSubkey(config *packet.Config) error {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
subPrivRaw, err := newDecrypter(config)
if err != nil {
return err
}
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
subkey := Subkey{
PublicKey: &sub.PublicKey,
PrivateKey: sub,
Sig: &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: creationTime,
KeyLifetimeSecs: &keyLifetimeSecs,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
Hash: config.Hash(),
FlagsValid: true,
FlagEncryptStorage: true,
FlagEncryptCommunications: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
},
}
if config != nil && config.V5Keys {
subkey.PublicKey.UpgradeToV5()
}
subkey.PublicKey.IsSubkey = true
subkey.PrivateKey.IsSubkey = true
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
return err
}
e.Subkeys = append(e.Subkeys, subkey)
return nil
}
// Generates a signing key
func newSigner(config *packet.Config) (signer crypto.Signer, err error) {
switch config.PublicKeyAlgorithm() {
case packet.PubKeyAlgoRSA:
bits := config.RSAModulusBits()
if bits < 1024 {
return nil, errors.InvalidArgumentError("bits must be >= 1024")
}
if config != nil && len(config.RSAPrimes) >= 2 {
primes := config.RSAPrimes[0:2]
config.RSAPrimes = config.RSAPrimes[2:]
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
}
return rsa.GenerateKey(config.Random(), bits)
case packet.PubKeyAlgoEdDSA:
_, priv, err := ed25519.GenerateKey(config.Random())
if err != nil {
return nil, err
}
return &priv, nil
default:
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
}
}
// Generates an encryption/decryption key
func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
switch config.PublicKeyAlgorithm() {
case packet.PubKeyAlgoRSA:
bits := config.RSAModulusBits()
if bits < 1024 {
return nil, errors.InvalidArgumentError("bits must be >= 1024")
}
if config != nil && len(config.RSAPrimes) >= 2 {
primes := config.RSAPrimes[0:2]
config.RSAPrimes = config.RSAPrimes[2:]
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
}
return rsa.GenerateKey(config.Random(), bits)
case packet.PubKeyAlgoEdDSA:
fallthrough // When passing EdDSA, we generate an ECDH subkey
case packet.PubKeyAlgoECDH:
var kdf = ecdh.KDF{
Hash: algorithm.SHA512,
Cipher: algorithm.AES256,
}
return ecdh.X25519GenerateKey(config.Random(), kdf)
default:
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
}
}
var bigOne = big.NewInt(1)
// generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the
// given bit size, using the given random source and prepopulated primes.
func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) {
priv := new(rsa.PrivateKey)
priv.E = 65537
if nprimes < 2 {
return nil, goerrors.New("generateRSAKeyWithPrimes: nprimes must be >= 2")
}
if bits < 1024 {
return nil, goerrors.New("generateRSAKeyWithPrimes: bits must be >= 1024")
}
primes := make([]*big.Int, nprimes)
NextSetOfPrimes:
for {
todo := bits
// crypto/rand should set the top two bits in each prime.
// Thus each prime has the form
// p_i = 2^bitlen(p_i) × 0.11... (in base 2).
// And the product is:
// P = 2^todo × α
// where α is the product of nprimes numbers of the form 0.11...
//
// If α < 1/2 (which can happen for nprimes > 2), we need to
// shift todo to compensate for lost bits: the mean value of 0.11...
// is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
// will give good results.
if nprimes >= 7 {
todo += (nprimes - 2) / 5
}
for i := 0; i < nprimes; i++ {
var err error
if len(prepopulatedPrimes) == 0 {
primes[i], err = rand.Prime(random, todo/(nprimes-i))
if err != nil {
return nil, err
}
} else {
primes[i] = prepopulatedPrimes[0]
prepopulatedPrimes = prepopulatedPrimes[1:]
}
todo -= primes[i].BitLen()
}
// Make sure that primes is pairwise unequal.
for i, prime := range primes {
for j := 0; j < i; j++ {
if prime.Cmp(primes[j]) == 0 {
continue NextSetOfPrimes
}
}
}
n := new(big.Int).Set(bigOne)
totient := new(big.Int).Set(bigOne)
pminus1 := new(big.Int)
for _, prime := range primes {
n.Mul(n, prime)
pminus1.Sub(prime, bigOne)
totient.Mul(totient, pminus1)
}
if n.BitLen() != bits {
// This should never happen for nprimes == 2 because
// crypto/rand should set the top two bits in each prime.
// For nprimes > 2 we hope it does not happen often.
continue NextSetOfPrimes
}
priv.D = new(big.Int)
e := big.NewInt(int64(priv.E))
ok := priv.D.ModInverse(e, totient)
if ok != nil {
priv.Primes = primes
priv.N = n
break
}
}
priv.Precompute()
return priv, nil
}
This diff is collapsed.
// Copyright (C) 2019 ProtonTech AG
package packet
import "math/bits"
// AEADConfig collects a number of AEAD parameters along with sensible defaults.
// A nil AEADConfig is valid and results in all default values.
type AEADConfig struct {
// The AEAD mode of operation.
DefaultMode AEADMode
// Amount of octets in each chunk of data
ChunkSize uint64
}
// Mode returns the AEAD mode of operation.
func (conf *AEADConfig) Mode() AEADMode {
if conf == nil || conf.DefaultMode == 0 {
return AEADModeEAX
}
mode := conf.DefaultMode
if mode != AEADModeEAX && mode != AEADModeOCB &&
mode != AEADModeExperimentalGCM {
panic("AEAD mode unsupported")
}
return mode
}
// ChunkSizeByte returns the byte indicating the chunk size. The effective
// chunk size is computed with the formula uint64(1) << (chunkSizeByte + 6)
func (conf *AEADConfig) ChunkSizeByte() byte {
if conf == nil || conf.ChunkSize == 0 {
return 12 // 1 << (12 + 6) == 262144 bytes
}
chunkSize := conf.ChunkSize
exponent := bits.Len64(chunkSize) - 1
switch {
case exponent < 6:
exponent = 6
case exponent > 27:
exponent = 27
}
return byte(exponent - 6)
}
// decodeAEADChunkSize returns the effective chunk size. In 32-bit systems, the
// maximum returned value is 1 << 30.
func decodeAEADChunkSize(c byte) int {
size := uint64(1 << (c + 6))
if size != uint64(int(size)) {
return 1 << 30
}
return int(size)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment