mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-28 16:02:01 +03:00
Bump go-git
This commit is contained in:
153
vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go
generated
vendored
Normal file
153
vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright 2014 Matthew Endsley
|
||||
// All rights reserved
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted providing that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package keywrap is an implementation of the RFC 3394 AES key wrapping
|
||||
// algorithm. This is used in OpenPGP with elliptic curve keys.
|
||||
package keywrap
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrWrapPlaintext is returned if the plaintext is not a multiple
|
||||
// of 64 bits.
|
||||
ErrWrapPlaintext = errors.New("keywrap: plainText must be a multiple of 64 bits")
|
||||
|
||||
// ErrUnwrapCiphertext is returned if the ciphertext is not a
|
||||
// multiple of 64 bits.
|
||||
ErrUnwrapCiphertext = errors.New("keywrap: cipherText must by a multiple of 64 bits")
|
||||
|
||||
// ErrUnwrapFailed is returned if unwrapping a key fails.
|
||||
ErrUnwrapFailed = errors.New("keywrap: failed to unwrap key")
|
||||
|
||||
// NB: the AES NewCipher call only fails if the key is an invalid length.
|
||||
|
||||
// ErrInvalidKey is returned when the AES key is invalid.
|
||||
ErrInvalidKey = errors.New("keywrap: invalid AES key")
|
||||
)
|
||||
|
||||
// Wrap a key using the RFC 3394 AES Key Wrap Algorithm.
|
||||
func Wrap(key, plainText []byte) ([]byte, error) {
|
||||
if len(plainText)%8 != 0 {
|
||||
return nil, ErrWrapPlaintext
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
nblocks := len(plainText) / 8
|
||||
|
||||
// 1) Initialize variables.
|
||||
var block [aes.BlockSize]byte
|
||||
// - Set A = IV, an initial value (see 2.2.3)
|
||||
for ii := 0; ii < 8; ii++ {
|
||||
block[ii] = 0xA6
|
||||
}
|
||||
|
||||
// - For i = 1 to n
|
||||
// - Set R[i] = P[i]
|
||||
intermediate := make([]byte, len(plainText))
|
||||
copy(intermediate, plainText)
|
||||
|
||||
// 2) Calculate intermediate values.
|
||||
for ii := 0; ii < 6; ii++ {
|
||||
for jj := 0; jj < nblocks; jj++ {
|
||||
// - B = AES(K, A | R[i])
|
||||
copy(block[8:], intermediate[jj*8:jj*8+8])
|
||||
c.Encrypt(block[:], block[:])
|
||||
|
||||
// - A = MSB(64, B) ^ t where t = (n*j)+1
|
||||
t := uint64(ii*nblocks + jj + 1)
|
||||
val := binary.BigEndian.Uint64(block[:8]) ^ t
|
||||
binary.BigEndian.PutUint64(block[:8], val)
|
||||
|
||||
// - R[i] = LSB(64, B)
|
||||
copy(intermediate[jj*8:jj*8+8], block[8:])
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Output results.
|
||||
// - Set C[0] = A
|
||||
// - For i = 1 to n
|
||||
// - C[i] = R[i]
|
||||
return append(block[:8], intermediate...), nil
|
||||
}
|
||||
|
||||
// Unwrap a key using the RFC 3394 AES Key Wrap Algorithm.
|
||||
func Unwrap(key, cipherText []byte) ([]byte, error) {
|
||||
if len(cipherText)%8 != 0 {
|
||||
return nil, ErrUnwrapCiphertext
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
nblocks := len(cipherText)/8 - 1
|
||||
|
||||
// 1) Initialize variables.
|
||||
var block [aes.BlockSize]byte
|
||||
// - Set A = C[0]
|
||||
copy(block[:8], cipherText[:8])
|
||||
|
||||
// - For i = 1 to n
|
||||
// - Set R[i] = C[i]
|
||||
intermediate := make([]byte, len(cipherText)-8)
|
||||
copy(intermediate, cipherText[8:])
|
||||
|
||||
// 2) Compute intermediate values.
|
||||
for jj := 5; jj >= 0; jj-- {
|
||||
for ii := nblocks - 1; ii >= 0; ii-- {
|
||||
// - B = AES-1(K, (A ^ t) | R[i]) where t = n*j+1
|
||||
// - A = MSB(64, B)
|
||||
t := uint64(jj*nblocks + ii + 1)
|
||||
val := binary.BigEndian.Uint64(block[:8]) ^ t
|
||||
binary.BigEndian.PutUint64(block[:8], val)
|
||||
|
||||
copy(block[8:], intermediate[ii*8:ii*8+8])
|
||||
c.Decrypt(block[:], block[:])
|
||||
|
||||
// - R[i] = LSB(B, 64)
|
||||
copy(intermediate[ii*8:ii*8+8], block[8:])
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Output results.
|
||||
// - If A is an appropriate initial value (see 2.2.3),
|
||||
for ii := 0; ii < 8; ii++ {
|
||||
if block[ii] != 0xA6 {
|
||||
return nil, ErrUnwrapFailed
|
||||
}
|
||||
}
|
||||
|
||||
// - For i = 1 to n
|
||||
// - P[i] = R[i]
|
||||
return intermediate, nil
|
||||
}
|
183
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
generated
vendored
Normal file
183
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
// 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 armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
|
||||
// very similar to PEM except that it has an additional CRC checksum.
|
||||
package armor // import "github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// A Block represents an OpenPGP armored structure.
|
||||
//
|
||||
// The encoded form is:
|
||||
//
|
||||
// -----BEGIN Type-----
|
||||
// Headers
|
||||
//
|
||||
// base64-encoded Bytes
|
||||
// '=' base64 encoded checksum (optional) not checked anymore
|
||||
// -----END Type-----
|
||||
//
|
||||
// where Headers is a possibly empty sequence of Key: Value lines.
|
||||
//
|
||||
// Since the armored data can be very large, this package presents a streaming
|
||||
// interface.
|
||||
type Block struct {
|
||||
Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE").
|
||||
Header map[string]string // Optional headers.
|
||||
Body io.Reader // A Reader from which the contents can be read
|
||||
lReader lineReader
|
||||
oReader openpgpReader
|
||||
}
|
||||
|
||||
var ArmorCorrupt error = errors.StructuralError("armor invalid")
|
||||
|
||||
var armorStart = []byte("-----BEGIN ")
|
||||
var armorEnd = []byte("-----END ")
|
||||
var armorEndOfLine = []byte("-----")
|
||||
|
||||
// lineReader wraps a line based reader. It watches for the end of an armor block
|
||||
type lineReader struct {
|
||||
in *bufio.Reader
|
||||
buf []byte
|
||||
eof bool
|
||||
}
|
||||
|
||||
func (l *lineReader) Read(p []byte) (n int, err error) {
|
||||
if l.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(l.buf) > 0 {
|
||||
n = copy(p, l.buf)
|
||||
l.buf = l.buf[n:]
|
||||
return
|
||||
}
|
||||
|
||||
line, isPrefix, err := l.in.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isPrefix {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(line, armorEnd) {
|
||||
l.eof = true
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(line) == 5 && line[0] == '=' {
|
||||
// This is the checksum line
|
||||
// Don't check the checksum
|
||||
|
||||
l.eof = true
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(line) > 96 {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
n = copy(p, line)
|
||||
bytesToSave := len(line) - n
|
||||
if bytesToSave > 0 {
|
||||
if cap(l.buf) < bytesToSave {
|
||||
l.buf = make([]byte, 0, bytesToSave)
|
||||
}
|
||||
l.buf = l.buf[0:bytesToSave]
|
||||
copy(l.buf, line[n:])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// openpgpReader passes Read calls to the underlying base64 decoder.
|
||||
type openpgpReader struct {
|
||||
lReader *lineReader
|
||||
b64Reader io.Reader
|
||||
}
|
||||
|
||||
func (r *openpgpReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.b64Reader.Read(p)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode reads a PGP armored block from the given Reader. It will ignore
|
||||
// leading garbage. If it doesn't find a block, it will return nil, io.EOF. The
|
||||
// given Reader is not usable after calling this function: an arbitrary amount
|
||||
// of data may have been read past the end of the block.
|
||||
func Decode(in io.Reader) (p *Block, err error) {
|
||||
r := bufio.NewReaderSize(in, 100)
|
||||
var line []byte
|
||||
ignoreNext := false
|
||||
|
||||
TryNextBlock:
|
||||
p = nil
|
||||
|
||||
// Skip leading garbage
|
||||
for {
|
||||
ignoreThis := ignoreNext
|
||||
line, ignoreNext, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ignoreNext || ignoreThis {
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
p = new(Block)
|
||||
p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
|
||||
p.Header = make(map[string]string)
|
||||
nextIsContinuation := false
|
||||
var lastKey string
|
||||
|
||||
// Read headers
|
||||
for {
|
||||
isContinuation := nextIsContinuation
|
||||
line, nextIsContinuation, err = r.ReadLine()
|
||||
if err != nil {
|
||||
p = nil
|
||||
return
|
||||
}
|
||||
if isContinuation {
|
||||
p.Header[lastKey] += string(line)
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
i := bytes.Index(line, []byte(":"))
|
||||
if i == -1 {
|
||||
goto TryNextBlock
|
||||
}
|
||||
lastKey = string(line[:i])
|
||||
var value string
|
||||
if len(line) > i+2 {
|
||||
value = string(line[i+2:])
|
||||
}
|
||||
p.Header[lastKey] = value
|
||||
}
|
||||
|
||||
p.lReader.in = r
|
||||
p.oReader.lReader = &p.lReader
|
||||
p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
|
||||
p.Body = &p.oReader
|
||||
|
||||
return
|
||||
}
|
206
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
generated
vendored
Normal file
206
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
generated
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
// 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 armor
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var armorHeaderSep = []byte(": ")
|
||||
var blockEnd = []byte("\n=")
|
||||
var newline = []byte("\n")
|
||||
var armorEndOfLineOut = []byte("-----\n")
|
||||
|
||||
const crc24Init = 0xb704ce
|
||||
const crc24Poly = 0x1864cfb
|
||||
|
||||
// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
|
||||
func crc24(crc uint32, d []byte) uint32 {
|
||||
for _, b := range d {
|
||||
crc ^= uint32(b) << 16
|
||||
for i := 0; i < 8; i++ {
|
||||
crc <<= 1
|
||||
if crc&0x1000000 != 0 {
|
||||
crc ^= crc24Poly
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
// writeSlices writes its arguments to the given Writer.
|
||||
func writeSlices(out io.Writer, slices ...[]byte) (err error) {
|
||||
for _, s := range slices {
|
||||
_, err = out.Write(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// lineBreaker breaks data across several lines, all of the same byte length
|
||||
// (except possibly the last). Lines are broken with a single '\n'.
|
||||
type lineBreaker struct {
|
||||
lineLength int
|
||||
line []byte
|
||||
used int
|
||||
out io.Writer
|
||||
haveWritten bool
|
||||
}
|
||||
|
||||
func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
|
||||
return &lineBreaker{
|
||||
lineLength: lineLength,
|
||||
line: make([]byte, lineLength),
|
||||
used: 0,
|
||||
out: out,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lineBreaker) Write(b []byte) (n int, err error) {
|
||||
n = len(b)
|
||||
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if l.used == 0 && l.haveWritten {
|
||||
_, err = l.out.Write([]byte{'\n'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.used+len(b) < l.lineLength {
|
||||
l.used += copy(l.line[l.used:], b)
|
||||
return
|
||||
}
|
||||
|
||||
l.haveWritten = true
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
excess := l.lineLength - l.used
|
||||
l.used = 0
|
||||
|
||||
_, err = l.out.Write(b[0:excess])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = l.Write(b[excess:])
|
||||
return
|
||||
}
|
||||
|
||||
func (l *lineBreaker) Close() (err error) {
|
||||
if l.used > 0 {
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// encoding keeps track of a running CRC24 over the data which has been written
|
||||
// to it and outputs a OpenPGP checksum when closed, followed by an armor
|
||||
// trailer.
|
||||
//
|
||||
// It's built into a stack of io.Writers:
|
||||
//
|
||||
// encoding -> base64 encoder -> lineBreaker -> out
|
||||
type encoding struct {
|
||||
out io.Writer
|
||||
breaker *lineBreaker
|
||||
b64 io.WriteCloser
|
||||
crc uint32
|
||||
crcEnabled bool
|
||||
blockType []byte
|
||||
}
|
||||
|
||||
func (e *encoding) Write(data []byte) (n int, err error) {
|
||||
if e.crcEnabled {
|
||||
e.crc = crc24(e.crc, data)
|
||||
}
|
||||
return e.b64.Write(data)
|
||||
}
|
||||
|
||||
func (e *encoding) Close() (err error) {
|
||||
err = e.b64.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.breaker.Close()
|
||||
|
||||
if e.crcEnabled {
|
||||
var checksumBytes [3]byte
|
||||
checksumBytes[0] = byte(e.crc >> 16)
|
||||
checksumBytes[1] = byte(e.crc >> 8)
|
||||
checksumBytes[2] = byte(e.crc)
|
||||
|
||||
var b64ChecksumBytes [4]byte
|
||||
base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
|
||||
|
||||
return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
|
||||
}
|
||||
return writeSlices(e.out, newline, armorEnd, e.blockType, armorEndOfLine)
|
||||
}
|
||||
|
||||
func encode(out io.Writer, blockType string, headers map[string]string, checksum bool) (w io.WriteCloser, err error) {
|
||||
bType := []byte(blockType)
|
||||
err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
keys := make([]string, len(headers))
|
||||
i := 0
|
||||
for k := range headers {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
err = writeSlices(out, []byte(k), armorHeaderSep, []byte(headers[k]), newline)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(newline)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e := &encoding{
|
||||
out: out,
|
||||
breaker: newLineBreaker(out, 64),
|
||||
blockType: bType,
|
||||
crc: crc24Init,
|
||||
crcEnabled: checksum,
|
||||
}
|
||||
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// Encode returns a WriteCloser which will encode the data written to it in
|
||||
// OpenPGP armor.
|
||||
func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
|
||||
return encode(out, blockType, headers, true)
|
||||
}
|
||||
|
||||
// EncodeWithChecksumOption returns a WriteCloser which will encode the data written to it in
|
||||
// OpenPGP armor and provides the option to include a checksum.
|
||||
// When forming ASCII Armor, the CRC24 footer SHOULD NOT be generated,
|
||||
// unless interoperability with implementations that require the CRC24 footer
|
||||
// to be present is a concern.
|
||||
func EncodeWithChecksumOption(out io.Writer, blockType string, headers map[string]string, doChecksum bool) (w io.WriteCloser, err error) {
|
||||
return encode(out, blockType, headers, doChecksum)
|
||||
}
|
71
vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
generated
vendored
Normal file
71
vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// 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' {
|
||||
if _, err := cw.Write(buf[start:i]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := cw.Write(newline); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
start = i + 1
|
||||
}
|
||||
case 1:
|
||||
*s = 0
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := cw.Write(buf[start:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
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()
|
||||
}
|
206
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
generated
vendored
Normal file
206
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
generated
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
// 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"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"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 {
|
||||
curve ecc.ECDHCurve
|
||||
Point []byte
|
||||
KDF
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
D []byte
|
||||
}
|
||||
|
||||
func NewPublicKey(curve ecc.ECDHCurve, kdfHash algorithm.Hash, kdfCipher algorithm.Cipher) *PublicKey {
|
||||
return &PublicKey{
|
||||
curve: curve,
|
||||
KDF: KDF{
|
||||
Hash: kdfHash,
|
||||
Cipher: kdfCipher,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (pk *PublicKey) GetCurve() ecc.ECDHCurve {
|
||||
return pk.curve
|
||||
}
|
||||
|
||||
func (pk *PublicKey) MarshalPoint() []byte {
|
||||
return pk.curve.MarshalBytePoint(pk.Point)
|
||||
}
|
||||
|
||||
func (pk *PublicKey) UnmarshalPoint(p []byte) error {
|
||||
pk.Point = pk.curve.UnmarshalBytePoint(p)
|
||||
if pk.Point == nil {
|
||||
return errors.New("ecdh: failed to parse EC point")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sk *PrivateKey) MarshalByteSecret() []byte {
|
||||
return sk.curve.MarshalByteSecret(sk.D)
|
||||
}
|
||||
|
||||
func (sk *PrivateKey) UnmarshalByteSecret(d []byte) error {
|
||||
sk.D = sk.curve.UnmarshalByteSecret(d)
|
||||
|
||||
if sk.D == nil {
|
||||
return errors.New("ecdh: failed to parse scalar")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateKey(rand io.Reader, c ecc.ECDHCurve, kdf KDF) (priv *PrivateKey, err error) {
|
||||
priv = new(PrivateKey)
|
||||
priv.PublicKey.curve = c
|
||||
priv.PublicKey.KDF = kdf
|
||||
priv.PublicKey.Point, priv.D, err = c.GenerateECDH(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...)
|
||||
|
||||
ephemeral, zb, err := pub.curve.Encaps(random, pub.Point)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
vsG = pub.curve.MarshalBytePoint(ephemeral)
|
||||
|
||||
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, c, curveOID, fingerprint []byte) (msg []byte, err error) {
|
||||
var m []byte
|
||||
zb, err := priv.PublicKey.curve.Decaps(priv.curve.UnmarshalBytePoint(vsG), priv.D)
|
||||
|
||||
// Try buildKey three times to workaround an old bug, see comments in buildKey.
|
||||
for i := 0; i < 3; i++ {
|
||||
var z []byte
|
||||
// RFC6637 §8: "Compute Z = KDF( S, Z_len, Param );"
|
||||
z, err = buildKey(&priv.PublicKey, zb, curveOID, fingerprint, i == 1, i == 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RFC6637 §8: "Compute C = AESKeyWrap( Z, c ) as per [RFC3394]"
|
||||
m, err = keywrap.Unwrap(z, c)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Only return an error after we've tried all (required) variants of buildKey.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RFC6637 §8: "m = symm_alg_ID || session key || checksum || pkcs5_padding"
|
||||
// The last byte should be the length of the padding, as per PKCS5; strip it off.
|
||||
return m[:len(m)-int(m[len(m)-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
|
||||
}
|
||||
if _, err := param.Write(fingerprint[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
}
|
||||
|
||||
func Validate(priv *PrivateKey) error {
|
||||
return priv.curve.ValidateECDH(priv.Point, priv.D)
|
||||
}
|
80
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdsa/ecdsa.go
generated
vendored
Normal file
80
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdsa/ecdsa.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
// Package ecdsa implements ECDSA signature, suitable for OpenPGP,
|
||||
// as specified in RFC 6637, section 5.
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
X, Y *big.Int
|
||||
curve ecc.ECDSACurve
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
D *big.Int
|
||||
}
|
||||
|
||||
func NewPublicKey(curve ecc.ECDSACurve) *PublicKey {
|
||||
return &PublicKey{
|
||||
curve: curve,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (pk *PublicKey) GetCurve() ecc.ECDSACurve {
|
||||
return pk.curve
|
||||
}
|
||||
|
||||
func (pk *PublicKey) MarshalPoint() []byte {
|
||||
return pk.curve.MarshalIntegerPoint(pk.X, pk.Y)
|
||||
}
|
||||
|
||||
func (pk *PublicKey) UnmarshalPoint(p []byte) error {
|
||||
pk.X, pk.Y = pk.curve.UnmarshalIntegerPoint(p)
|
||||
if pk.X == nil {
|
||||
return errors.New("ecdsa: failed to parse EC point")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sk *PrivateKey) MarshalIntegerSecret() []byte {
|
||||
return sk.curve.MarshalIntegerSecret(sk.D)
|
||||
}
|
||||
|
||||
func (sk *PrivateKey) UnmarshalIntegerSecret(d []byte) error {
|
||||
sk.D = sk.curve.UnmarshalIntegerSecret(d)
|
||||
|
||||
if sk.D == nil {
|
||||
return errors.New("ecdsa: failed to parse scalar")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateKey(rand io.Reader, c ecc.ECDSACurve) (priv *PrivateKey, err error) {
|
||||
priv = new(PrivateKey)
|
||||
priv.PublicKey.curve = c
|
||||
priv.PublicKey.X, priv.PublicKey.Y, priv.D, err = c.GenerateECDSA(rand)
|
||||
return
|
||||
}
|
||||
|
||||
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
|
||||
return priv.PublicKey.curve.Sign(rand, priv.X, priv.Y, priv.D, hash)
|
||||
}
|
||||
|
||||
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
return pub.curve.Verify(pub.X, pub.Y, hash, r, s)
|
||||
}
|
||||
|
||||
func Validate(priv *PrivateKey) error {
|
||||
return priv.curve.ValidateECDSA(priv.X, priv.Y, priv.D.Bytes())
|
||||
}
|
115
vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go
generated
vendored
Normal file
115
vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
// Package ed25519 implements the ed25519 signature algorithm for OpenPGP
|
||||
// as defined in the Open PGP crypto refresh.
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
ed25519lib "github.com/cloudflare/circl/sign/ed25519"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys in this package.
|
||||
PublicKeySize = ed25519lib.PublicKeySize
|
||||
// SeedSize is the size, in bytes, of private key seeds.
|
||||
// The private key representation used by RFC 8032.
|
||||
SeedSize = ed25519lib.SeedSize
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = ed25519lib.SignatureSize
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
// Point represents the elliptic curve point of the public key.
|
||||
Point []byte
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
// Key the private key representation by RFC 8032,
|
||||
// encoded as seed | pub key point.
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// NewPublicKey creates a new empty ed25519 public key.
|
||||
func NewPublicKey() *PublicKey {
|
||||
return &PublicKey{}
|
||||
}
|
||||
|
||||
// NewPrivateKey creates a new empty private key referencing the public key.
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
// Seed returns the ed25519 private key secret seed.
|
||||
// The private key representation by RFC 8032.
|
||||
func (pk *PrivateKey) Seed() []byte {
|
||||
return pk.Key[:SeedSize]
|
||||
}
|
||||
|
||||
// MarshalByteSecret returns the underlying 32 byte seed of the private key.
|
||||
func (pk *PrivateKey) MarshalByteSecret() []byte {
|
||||
return pk.Seed()
|
||||
}
|
||||
|
||||
// UnmarshalByteSecret computes the private key from the secret seed
|
||||
// and stores it in the private key object.
|
||||
func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error {
|
||||
sk.Key = ed25519lib.NewKeyFromSeed(seed)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a fresh private key with the provided randomness source.
|
||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||
publicKey, privateKey, err := ed25519lib.GenerateKey(rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privateKeyOut := new(PrivateKey)
|
||||
privateKeyOut.PublicKey.Point = publicKey[:]
|
||||
privateKeyOut.Key = privateKey[:]
|
||||
return privateKeyOut, nil
|
||||
}
|
||||
|
||||
// Sign signs a message with the ed25519 algorithm.
|
||||
// priv MUST be a valid key! Check this with Validate() before use.
|
||||
func Sign(priv *PrivateKey, message []byte) ([]byte, error) {
|
||||
return ed25519lib.Sign(priv.Key, message), nil
|
||||
}
|
||||
|
||||
// Verify verifies an ed25519 signature.
|
||||
func Verify(pub *PublicKey, message []byte, signature []byte) bool {
|
||||
return ed25519lib.Verify(pub.Point, message, signature)
|
||||
}
|
||||
|
||||
// Validate checks if the ed25519 private key is valid.
|
||||
func Validate(priv *PrivateKey) error {
|
||||
expectedPrivateKey := ed25519lib.NewKeyFromSeed(priv.Seed())
|
||||
if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 {
|
||||
return errors.KeyInvalidError("ed25519: invalid ed25519 secret")
|
||||
}
|
||||
if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 {
|
||||
return errors.KeyInvalidError("ed25519: invalid ed25519 public key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENCODING/DECODING signature:
|
||||
|
||||
// WriteSignature encodes and writes an ed25519 signature to writer.
|
||||
func WriteSignature(writer io.Writer, signature []byte) error {
|
||||
_, err := writer.Write(signature)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadSignature decodes an ed25519 signature from a reader.
|
||||
func ReadSignature(reader io.Reader) ([]byte, error) {
|
||||
signature := make([]byte, SignatureSize)
|
||||
if _, err := io.ReadFull(reader, signature); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signature, nil
|
||||
}
|
119
vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go
generated
vendored
Normal file
119
vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
// Package ed448 implements the ed448 signature algorithm for OpenPGP
|
||||
// as defined in the Open PGP crypto refresh.
|
||||
package ed448
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
ed448lib "github.com/cloudflare/circl/sign/ed448"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys in this package.
|
||||
PublicKeySize = ed448lib.PublicKeySize
|
||||
// SeedSize is the size, in bytes, of private key seeds.
|
||||
// The private key representation used by RFC 8032.
|
||||
SeedSize = ed448lib.SeedSize
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = ed448lib.SignatureSize
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
// Point represents the elliptic curve point of the public key.
|
||||
Point []byte
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
// Key the private key representation by RFC 8032,
|
||||
// encoded as seed | public key point.
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// NewPublicKey creates a new empty ed448 public key.
|
||||
func NewPublicKey() *PublicKey {
|
||||
return &PublicKey{}
|
||||
}
|
||||
|
||||
// NewPrivateKey creates a new empty private key referencing the public key.
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
// Seed returns the ed448 private key secret seed.
|
||||
// The private key representation by RFC 8032.
|
||||
func (pk *PrivateKey) Seed() []byte {
|
||||
return pk.Key[:SeedSize]
|
||||
}
|
||||
|
||||
// MarshalByteSecret returns the underlying seed of the private key.
|
||||
func (pk *PrivateKey) MarshalByteSecret() []byte {
|
||||
return pk.Seed()
|
||||
}
|
||||
|
||||
// UnmarshalByteSecret computes the private key from the secret seed
|
||||
// and stores it in the private key object.
|
||||
func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error {
|
||||
sk.Key = ed448lib.NewKeyFromSeed(seed)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a fresh private key with the provided randomness source.
|
||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||
publicKey, privateKey, err := ed448lib.GenerateKey(rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privateKeyOut := new(PrivateKey)
|
||||
privateKeyOut.PublicKey.Point = publicKey[:]
|
||||
privateKeyOut.Key = privateKey[:]
|
||||
return privateKeyOut, nil
|
||||
}
|
||||
|
||||
// Sign signs a message with the ed448 algorithm.
|
||||
// priv MUST be a valid key! Check this with Validate() before use.
|
||||
func Sign(priv *PrivateKey, message []byte) ([]byte, error) {
|
||||
// Ed448 is used with the empty string as a context string.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7
|
||||
return ed448lib.Sign(priv.Key, message, ""), nil
|
||||
}
|
||||
|
||||
// Verify verifies a ed448 signature
|
||||
func Verify(pub *PublicKey, message []byte, signature []byte) bool {
|
||||
// Ed448 is used with the empty string as a context string.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7
|
||||
return ed448lib.Verify(pub.Point, message, signature, "")
|
||||
}
|
||||
|
||||
// Validate checks if the ed448 private key is valid
|
||||
func Validate(priv *PrivateKey) error {
|
||||
expectedPrivateKey := ed448lib.NewKeyFromSeed(priv.Seed())
|
||||
if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 {
|
||||
return errors.KeyInvalidError("ed448: invalid ed448 secret")
|
||||
}
|
||||
if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 {
|
||||
return errors.KeyInvalidError("ed448: invalid ed448 public key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENCODING/DECODING signature:
|
||||
|
||||
// WriteSignature encodes and writes an ed448 signature to writer.
|
||||
func WriteSignature(writer io.Writer, signature []byte) error {
|
||||
_, err := writer.Write(signature)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadSignature decodes an ed448 signature from a reader.
|
||||
func ReadSignature(reader io.Reader) ([]byte, error) {
|
||||
signature := make([]byte, SignatureSize)
|
||||
if _, err := io.ReadFull(reader, signature); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signature, nil
|
||||
}
|
91
vendor/github.com/ProtonMail/go-crypto/openpgp/eddsa/eddsa.go
generated
vendored
Normal file
91
vendor/github.com/ProtonMail/go-crypto/openpgp/eddsa/eddsa.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
// Package eddsa implements EdDSA signature, suitable for OpenPGP, as specified in
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
|
||||
package eddsa
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"io"
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
X []byte
|
||||
curve ecc.EdDSACurve
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
D []byte
|
||||
}
|
||||
|
||||
func NewPublicKey(curve ecc.EdDSACurve) *PublicKey {
|
||||
return &PublicKey{
|
||||
curve: curve,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (pk *PublicKey) GetCurve() ecc.EdDSACurve {
|
||||
return pk.curve
|
||||
}
|
||||
|
||||
func (pk *PublicKey) MarshalPoint() []byte {
|
||||
return pk.curve.MarshalBytePoint(pk.X)
|
||||
}
|
||||
|
||||
func (pk *PublicKey) UnmarshalPoint(x []byte) error {
|
||||
pk.X = pk.curve.UnmarshalBytePoint(x)
|
||||
|
||||
if pk.X == nil {
|
||||
return errors.New("eddsa: failed to parse EC point")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sk *PrivateKey) MarshalByteSecret() []byte {
|
||||
return sk.curve.MarshalByteSecret(sk.D)
|
||||
}
|
||||
|
||||
func (sk *PrivateKey) UnmarshalByteSecret(d []byte) error {
|
||||
sk.D = sk.curve.UnmarshalByteSecret(d)
|
||||
|
||||
if sk.D == nil {
|
||||
return errors.New("eddsa: failed to parse scalar")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateKey(rand io.Reader, c ecc.EdDSACurve) (priv *PrivateKey, err error) {
|
||||
priv = new(PrivateKey)
|
||||
priv.PublicKey.curve = c
|
||||
priv.PublicKey.X, priv.D, err = c.GenerateEdDSA(rand)
|
||||
return
|
||||
}
|
||||
|
||||
func Sign(priv *PrivateKey, message []byte) (r, s []byte, err error) {
|
||||
sig, err := priv.PublicKey.curve.Sign(priv.PublicKey.X, priv.D, message)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r, s = priv.PublicKey.curve.MarshalSignature(sig)
|
||||
return
|
||||
}
|
||||
|
||||
func Verify(pub *PublicKey, message, r, s []byte) bool {
|
||||
sig := pub.curve.UnmarshalSignature(r, s)
|
||||
if sig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return pub.curve.Verify(pub.X, message, sig)
|
||||
}
|
||||
|
||||
func Validate(priv *PrivateKey) error {
|
||||
return priv.curve.ValidateEdDSA(priv.PublicKey.X, priv.D)
|
||||
}
|
124
vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go
generated
vendored
Normal file
124
vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
// 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
|
||||
}
|
200
vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
generated
vendored
Normal file
200
vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
generated
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDecryptSessionKeyParsing is a generic error message for parsing errors in decrypted data
|
||||
// to reduce the risk of oracle attacks.
|
||||
ErrDecryptSessionKeyParsing = DecryptWithSessionKeyError("parsing error")
|
||||
// ErrAEADTagVerification is returned if one of the tag verifications in SEIPDv2 fails
|
||||
ErrAEADTagVerification error = DecryptWithSessionKeyError("AEAD tag verification failed")
|
||||
// ErrMDCHashMismatch
|
||||
ErrMDCHashMismatch error = SignatureError("MDC hash mismatch")
|
||||
// ErrMDCMissing
|
||||
ErrMDCMissing error = SignatureError("MDC packet not found")
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// A DecryptWithSessionKeyError is returned when a failure occurs when reading from symmetrically decrypted data or
|
||||
// an authentication tag verification fails.
|
||||
// Such an error indicates that the supplied session key is likely wrong or the data got corrupted.
|
||||
type DecryptWithSessionKeyError string
|
||||
|
||||
func (s DecryptWithSessionKeyError) Error() string {
|
||||
return "openpgp: decryption with session key failed: " + string(s)
|
||||
}
|
||||
|
||||
// HandleSensitiveParsingError handles parsing errors when reading data from potentially decrypted data.
|
||||
// The function makes parsing errors generic to reduce the risk of oracle attacks in SEIPDv1.
|
||||
func HandleSensitiveParsingError(err error, decrypted bool) error {
|
||||
if !decrypted {
|
||||
// Data was not encrypted so we return the inner error.
|
||||
return err
|
||||
}
|
||||
// The data is read from a stream that decrypts using a session key;
|
||||
// therefore, we need to handle parsing errors appropriately.
|
||||
// This is essential to mitigate the risk of oracle attacks.
|
||||
if decError, ok := err.(*DecryptWithSessionKeyError); ok {
|
||||
return decError
|
||||
}
|
||||
if decError, ok := err.(DecryptWithSessionKeyError); ok {
|
||||
return decError
|
||||
}
|
||||
return ErrDecryptSessionKeyParsing
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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 ErrSignatureOlderThanKey error = signatureOlderThanKeyError(0)
|
||||
|
||||
type signatureOlderThanKeyError int
|
||||
|
||||
func (ske signatureOlderThanKeyError) Error() string {
|
||||
return "openpgp: signature is older than the key"
|
||||
}
|
||||
|
||||
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 WeakAlgorithmError string
|
||||
|
||||
func (e WeakAlgorithmError) Error() string {
|
||||
return "openpgp: weak algorithms are rejected: " + string(e)
|
||||
}
|
||||
|
||||
type UnknownPacketTypeError uint8
|
||||
|
||||
func (upte UnknownPacketTypeError) Error() string {
|
||||
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
|
||||
}
|
||||
|
||||
type CriticalUnknownPacketTypeError uint8
|
||||
|
||||
func (upte CriticalUnknownPacketTypeError) Error() string {
|
||||
return "openpgp: unknown critical 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)
|
||||
}
|
||||
|
||||
// ErrMalformedMessage results when the packet sequence is incorrect
|
||||
type ErrMalformedMessage string
|
||||
|
||||
func (dke ErrMalformedMessage) Error() string {
|
||||
return "openpgp: malformed message " + string(dke)
|
||||
}
|
||||
|
||||
// ErrEncryptionKeySelection is returned if encryption key selection fails (v2 API).
|
||||
type ErrEncryptionKeySelection struct {
|
||||
PrimaryKeyId string
|
||||
PrimaryKeyErr error
|
||||
EncSelectionKeyId *string
|
||||
EncSelectionErr error
|
||||
}
|
||||
|
||||
func (eks ErrEncryptionKeySelection) Error() string {
|
||||
prefix := fmt.Sprintf("openpgp: key selection for primary key %s:", eks.PrimaryKeyId)
|
||||
if eks.PrimaryKeyErr != nil {
|
||||
return fmt.Sprintf("%s invalid primary key: %s", prefix, eks.PrimaryKeyErr)
|
||||
}
|
||||
if eks.EncSelectionKeyId != nil {
|
||||
return fmt.Sprintf("%s invalid encryption key %s: %s", prefix, *eks.EncSelectionKeyId, eks.EncSelectionErr)
|
||||
}
|
||||
return fmt.Sprintf("%s no encryption key: %s", prefix, eks.EncSelectionErr)
|
||||
}
|
24
vendor/github.com/ProtonMail/go-crypto/openpgp/hash.go
generated
vendored
Normal file
24
vendor/github.com/ProtonMail/go-crypto/openpgp/hash.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package openpgp
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP
|
||||
// hash id.
|
||||
func HashIdToHash(id byte) (h crypto.Hash, ok bool) {
|
||||
return algorithm.HashIdToHash(id)
|
||||
}
|
||||
|
||||
// HashIdToString returns the name of the hash function corresponding to the
|
||||
// given OpenPGP hash id.
|
||||
func HashIdToString(id byte) (name string, ok bool) {
|
||||
return algorithm.HashIdToString(id)
|
||||
}
|
||||
|
||||
// HashToHashId returns an OpenPGP hash id which corresponds the given Hash.
|
||||
func HashToHashId(h crypto.Hash) (id byte, ok bool) {
|
||||
return algorithm.HashToHashId(h)
|
||||
}
|
65
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go
generated
vendored
Normal file
65
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// 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(3)
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
97
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
generated
vendored
Normal file
97
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
// 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)
|
||||
}
|
||||
|
||||
// KeySize returns the key size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) KeySize() int {
|
||||
switch cipher {
|
||||
case CAST5:
|
||||
return cast5.KeySize
|
||||
case AES128:
|
||||
return 16
|
||||
case AES192, TripleDES:
|
||||
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
|
||||
}
|
143
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go
generated
vendored
Normal file
143
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
// 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 (
|
||||
SHA1 Hash = cryptoHash{2, crypto.SHA1}
|
||||
SHA256 Hash = cryptoHash{8, crypto.SHA256}
|
||||
SHA384 Hash = cryptoHash{9, crypto.SHA384}
|
||||
SHA512 Hash = cryptoHash{10, crypto.SHA512}
|
||||
SHA224 Hash = cryptoHash{11, crypto.SHA224}
|
||||
SHA3_256 Hash = cryptoHash{12, crypto.SHA3_256}
|
||||
SHA3_512 Hash = cryptoHash{14, crypto.SHA3_512}
|
||||
)
|
||||
|
||||
// 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{
|
||||
SHA256.Id(): SHA256,
|
||||
SHA384.Id(): SHA384,
|
||||
SHA512.Id(): SHA512,
|
||||
SHA224.Id(): SHA224,
|
||||
SHA3_256.Id(): SHA3_256,
|
||||
SHA3_512.Id(): SHA3_512,
|
||||
}
|
||||
)
|
||||
|
||||
// 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{
|
||||
SHA256.Id(): "SHA256",
|
||||
SHA384.Id(): "SHA384",
|
||||
SHA512.Id(): "SHA512",
|
||||
SHA224.Id(): "SHA224",
|
||||
SHA3_256.Id(): "SHA3-256",
|
||||
SHA3_512.Id(): "SHA3-512",
|
||||
}
|
||||
|
||||
func (h cryptoHash) String() string {
|
||||
s, ok := hashNames[h.id]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unsupported hash function %d", h.id))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP
|
||||
// hash id.
|
||||
func HashIdToHash(id byte) (h crypto.Hash, ok bool) {
|
||||
if hash, ok := HashById[id]; ok {
|
||||
return hash.HashFunc(), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// HashIdToHashWithSha1 returns a crypto.Hash which corresponds to the given OpenPGP
|
||||
// hash id, allowing sha1.
|
||||
func HashIdToHashWithSha1(id byte) (h crypto.Hash, ok bool) {
|
||||
if hash, ok := HashById[id]; ok {
|
||||
return hash.HashFunc(), true
|
||||
}
|
||||
|
||||
if id == SHA1.Id() {
|
||||
return SHA1.HashFunc(), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// HashIdToString returns the name of the hash function corresponding to the
|
||||
// given OpenPGP hash id.
|
||||
func HashIdToString(id byte) (name string, ok bool) {
|
||||
if hash, ok := HashById[id]; ok {
|
||||
return hash.String(), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// HashToHashId returns an OpenPGP hash id which corresponds the given Hash.
|
||||
func HashToHashId(h crypto.Hash) (id byte, ok bool) {
|
||||
for id, hash := range HashById {
|
||||
if hash.HashFunc() == h {
|
||||
return id, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// HashToHashIdWithSha1 returns an OpenPGP hash id which corresponds the given Hash,
|
||||
// allowing instances of SHA1
|
||||
func HashToHashIdWithSha1(h crypto.Hash) (id byte, ok bool) {
|
||||
for id, hash := range HashById {
|
||||
if hash.HashFunc() == h {
|
||||
return id, true
|
||||
}
|
||||
}
|
||||
|
||||
if h == SHA1.HashFunc() {
|
||||
return SHA1.Id(), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
171
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve25519.go
generated
vendored
Normal file
171
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve25519.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
x25519lib "github.com/cloudflare/circl/dh/x25519"
|
||||
)
|
||||
|
||||
type curve25519 struct{}
|
||||
|
||||
func NewCurve25519() *curve25519 {
|
||||
return &curve25519{}
|
||||
}
|
||||
|
||||
func (c *curve25519) GetCurveName() string {
|
||||
return "curve25519"
|
||||
}
|
||||
|
||||
// MarshalBytePoint encodes the public point from native format, adding the prefix.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
|
||||
func (c *curve25519) MarshalBytePoint(point []byte) []byte {
|
||||
return append([]byte{0x40}, point...)
|
||||
}
|
||||
|
||||
// UnmarshalBytePoint decodes the public point to native format, removing the prefix.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
|
||||
func (c *curve25519) UnmarshalBytePoint(point []byte) []byte {
|
||||
if len(point) != x25519lib.Size+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove prefix
|
||||
return point[1:]
|
||||
}
|
||||
|
||||
// MarshalByteSecret encodes the secret scalar from native format.
|
||||
// Note that the EC secret scalar 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 OpenPGP, and (2) the
|
||||
// leading zeros are truncated.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.1
|
||||
// Note that leading zero bytes are stripped later when encoding as an MPI.
|
||||
func (c *curve25519) MarshalByteSecret(secret []byte) []byte {
|
||||
d := make([]byte, x25519lib.Size)
|
||||
copyReversed(d, secret)
|
||||
|
||||
// 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
|
||||
// the curve.
|
||||
//
|
||||
// This masking is done internally in the underlying lib and so is unnecessary
|
||||
// for security, but OpenPGP implementations require that private keys be
|
||||
// pre-masked.
|
||||
d[0] &= 127
|
||||
d[0] |= 64
|
||||
d[31] &= 248
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// UnmarshalByteSecret decodes the secret scalar from native format.
|
||||
// Note that the EC secret scalar 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 OpenPGP, and (2) the
|
||||
// leading zeros are truncated.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.1
|
||||
func (c *curve25519) UnmarshalByteSecret(d []byte) []byte {
|
||||
if len(d) > x25519lib.Size {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure truncated leading bytes are re-added
|
||||
secret := make([]byte, x25519lib.Size)
|
||||
copyReversed(secret, d)
|
||||
|
||||
return secret
|
||||
}
|
||||
|
||||
// generateKeyPairBytes Generates a private-public key-pair.
|
||||
// 'priv' is a private key; a little-endian 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 (c *curve25519) generateKeyPairBytes(rand io.Reader) (priv, pub x25519lib.Key, err error) {
|
||||
_, err = io.ReadFull(rand, priv[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
x25519lib.KeyGen(&pub, &priv)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *curve25519) GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error) {
|
||||
priv, pub, err := c.generateKeyPairBytes(rand)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return pub[:], priv[:], nil
|
||||
}
|
||||
|
||||
func (c *genericCurve) MaskSecret(secret []byte) []byte {
|
||||
return secret
|
||||
}
|
||||
|
||||
func (c *curve25519) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
|
||||
// RFC6637 §8: "Generate an ephemeral key pair {v, V=vG}"
|
||||
// ephemeralPrivate corresponds to `v`.
|
||||
// ephemeralPublic corresponds to `V`.
|
||||
ephemeralPrivate, ephemeralPublic, err := c.generateKeyPairBytes(rand)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// RFC6637 §8: "Obtain the authenticated recipient public key R"
|
||||
// pubKey corresponds to `R`.
|
||||
var pubKey x25519lib.Key
|
||||
copy(pubKey[:], point)
|
||||
|
||||
// RFC6637 §8: "Compute the shared point S = vR"
|
||||
// "VB = convert point V to the octet string"
|
||||
// sharedPoint corresponds to `VB`.
|
||||
var sharedPoint x25519lib.Key
|
||||
x25519lib.Shared(&sharedPoint, &ephemeralPrivate, &pubKey)
|
||||
|
||||
return ephemeralPublic[:], sharedPoint[:], nil
|
||||
}
|
||||
|
||||
func (c *curve25519) Decaps(vsG, secret []byte) (sharedSecret []byte, err error) {
|
||||
var ephemeralPublic, decodedPrivate, sharedPoint x25519lib.Key
|
||||
// RFC6637 §8: "The decryption is the inverse of the method given."
|
||||
// All quoted descriptions in comments below describe encryption, and
|
||||
// the reverse is performed.
|
||||
// vsG corresponds to `VB` in RFC6637 §8 .
|
||||
|
||||
// RFC6637 §8: "VB = convert point V to the octet string"
|
||||
copy(ephemeralPublic[:], vsG)
|
||||
|
||||
// decodedPrivate corresponds to `r` in RFC6637 §8 .
|
||||
copy(decodedPrivate[:], secret)
|
||||
|
||||
// RFC6637 §8: "Note that the recipient obtains the shared secret by calculating
|
||||
// S = rV = rvG, where (r,R) is the recipient's key pair."
|
||||
// sharedPoint corresponds to `S`.
|
||||
x25519lib.Shared(&sharedPoint, &decodedPrivate, &ephemeralPublic)
|
||||
|
||||
return sharedPoint[:], nil
|
||||
}
|
||||
|
||||
func (c *curve25519) ValidateECDH(point []byte, secret []byte) (err error) {
|
||||
var pk, sk x25519lib.Key
|
||||
copy(sk[:], secret)
|
||||
x25519lib.KeyGen(&pk, &sk)
|
||||
|
||||
if subtle.ConstantTimeCompare(point, pk[:]) == 0 {
|
||||
return errors.KeyInvalidError("ecc: invalid curve25519 public point")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyReversed(out []byte, in []byte) {
|
||||
l := len(in)
|
||||
for i := 0; i < l; i++ {
|
||||
out[i] = in[l-i-1]
|
||||
}
|
||||
}
|
143
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go
generated
vendored
Normal file
143
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/bitcurves"
|
||||
"github.com/ProtonMail/go-crypto/brainpool"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
)
|
||||
|
||||
const Curve25519GenName = "Curve25519"
|
||||
|
||||
type CurveInfo struct {
|
||||
GenName string
|
||||
Oid *encoding.OID
|
||||
Curve Curve
|
||||
}
|
||||
|
||||
var Curves = []CurveInfo{
|
||||
{
|
||||
// NIST P-256
|
||||
GenName: "P256",
|
||||
Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}),
|
||||
Curve: NewGenericCurve(elliptic.P256()),
|
||||
},
|
||||
{
|
||||
// NIST P-384
|
||||
GenName: "P384",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}),
|
||||
Curve: NewGenericCurve(elliptic.P384()),
|
||||
},
|
||||
{
|
||||
// NIST P-521
|
||||
GenName: "P521",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}),
|
||||
Curve: NewGenericCurve(elliptic.P521()),
|
||||
},
|
||||
{
|
||||
// SecP256k1
|
||||
GenName: "SecP256k1",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}),
|
||||
Curve: NewGenericCurve(bitcurves.S256()),
|
||||
},
|
||||
{
|
||||
// Curve25519
|
||||
GenName: Curve25519GenName,
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}),
|
||||
Curve: NewCurve25519(),
|
||||
},
|
||||
{
|
||||
// x448
|
||||
GenName: "Curve448",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x65, 0x6F}),
|
||||
Curve: NewX448(),
|
||||
},
|
||||
{
|
||||
// Ed25519
|
||||
GenName: Curve25519GenName,
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}),
|
||||
Curve: NewEd25519(),
|
||||
},
|
||||
{
|
||||
// Ed448
|
||||
GenName: "Curve448",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x65, 0x71}),
|
||||
Curve: NewEd448(),
|
||||
},
|
||||
{
|
||||
// BrainpoolP256r1
|
||||
GenName: "BrainpoolP256",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}),
|
||||
Curve: NewGenericCurve(brainpool.P256r1()),
|
||||
},
|
||||
{
|
||||
// BrainpoolP384r1
|
||||
GenName: "BrainpoolP384",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}),
|
||||
Curve: NewGenericCurve(brainpool.P384r1()),
|
||||
},
|
||||
{
|
||||
// BrainpoolP512r1
|
||||
GenName: "BrainpoolP512",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}),
|
||||
Curve: NewGenericCurve(brainpool.P512r1()),
|
||||
},
|
||||
}
|
||||
|
||||
func FindByCurve(curve Curve) *CurveInfo {
|
||||
for _, curveInfo := range Curves {
|
||||
if curveInfo.Curve.GetCurveName() == curve.GetCurveName() {
|
||||
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 FindEdDSAByGenName(curveGenName string) EdDSACurve {
|
||||
for _, curveInfo := range Curves {
|
||||
if curveInfo.GenName == curveGenName {
|
||||
curve, ok := curveInfo.Curve.(EdDSACurve)
|
||||
if ok {
|
||||
return curve
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindECDSAByGenName(curveGenName string) ECDSACurve {
|
||||
for _, curveInfo := range Curves {
|
||||
if curveInfo.GenName == curveGenName {
|
||||
curve, ok := curveInfo.Curve.(ECDSACurve)
|
||||
if ok {
|
||||
return curve
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindECDHByGenName(curveGenName string) ECDHCurve {
|
||||
for _, curveInfo := range Curves {
|
||||
if curveInfo.GenName == curveGenName {
|
||||
curve, ok := curveInfo.Curve.(ECDHCurve)
|
||||
if ok {
|
||||
return curve
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
48
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curves.go
generated
vendored
Normal file
48
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curves.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Curve interface {
|
||||
GetCurveName() string
|
||||
}
|
||||
|
||||
type ECDSACurve interface {
|
||||
Curve
|
||||
MarshalIntegerPoint(x, y *big.Int) []byte
|
||||
UnmarshalIntegerPoint([]byte) (x, y *big.Int)
|
||||
MarshalIntegerSecret(d *big.Int) []byte
|
||||
UnmarshalIntegerSecret(d []byte) *big.Int
|
||||
GenerateECDSA(rand io.Reader) (x, y, secret *big.Int, err error)
|
||||
Sign(rand io.Reader, x, y, d *big.Int, hash []byte) (r, s *big.Int, err error)
|
||||
Verify(x, y *big.Int, hash []byte, r, s *big.Int) bool
|
||||
ValidateECDSA(x, y *big.Int, secret []byte) error
|
||||
}
|
||||
|
||||
type EdDSACurve interface {
|
||||
Curve
|
||||
MarshalBytePoint(x []byte) []byte
|
||||
UnmarshalBytePoint([]byte) (x []byte)
|
||||
MarshalByteSecret(d []byte) []byte
|
||||
UnmarshalByteSecret(d []byte) []byte
|
||||
MarshalSignature(sig []byte) (r, s []byte)
|
||||
UnmarshalSignature(r, s []byte) (sig []byte)
|
||||
GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error)
|
||||
Sign(publicKey, privateKey, message []byte) (sig []byte, err error)
|
||||
Verify(publicKey, message, sig []byte) bool
|
||||
ValidateEdDSA(publicKey, privateKey []byte) (err error)
|
||||
}
|
||||
type ECDHCurve interface {
|
||||
Curve
|
||||
MarshalBytePoint([]byte) (encoded []byte)
|
||||
UnmarshalBytePoint(encoded []byte) []byte
|
||||
MarshalByteSecret(d []byte) []byte
|
||||
UnmarshalByteSecret(d []byte) []byte
|
||||
GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error)
|
||||
Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error)
|
||||
Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error)
|
||||
ValidateECDH(public []byte, secret []byte) error
|
||||
}
|
120
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go
generated
vendored
Normal file
120
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
ed25519lib "github.com/cloudflare/circl/sign/ed25519"
|
||||
)
|
||||
|
||||
const ed25519Size = 32
|
||||
|
||||
type ed25519 struct{}
|
||||
|
||||
func NewEd25519() *ed25519 {
|
||||
return &ed25519{}
|
||||
}
|
||||
|
||||
func (c *ed25519) GetCurveName() string {
|
||||
return "ed25519"
|
||||
}
|
||||
|
||||
// MarshalBytePoint encodes the public point from native format, adding the prefix.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed25519) MarshalBytePoint(x []byte) []byte {
|
||||
return append([]byte{0x40}, x...)
|
||||
}
|
||||
|
||||
// UnmarshalBytePoint decodes a point from prefixed format to native.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed25519) UnmarshalBytePoint(point []byte) (x []byte) {
|
||||
if len(point) != ed25519lib.PublicKeySize+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return unprefixed
|
||||
return point[1:]
|
||||
}
|
||||
|
||||
// MarshalByteSecret encodes a scalar in native format.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed25519) MarshalByteSecret(d []byte) []byte {
|
||||
return d
|
||||
}
|
||||
|
||||
// UnmarshalByteSecret decodes a scalar in native format and re-adds the stripped leading zeroes
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed25519) UnmarshalByteSecret(s []byte) (d []byte) {
|
||||
if len(s) > ed25519lib.SeedSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle stripped leading zeroes
|
||||
d = make([]byte, ed25519lib.SeedSize)
|
||||
copy(d[ed25519lib.SeedSize-len(s):], s)
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalSignature splits a signature in R and S.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.1
|
||||
func (c *ed25519) MarshalSignature(sig []byte) (r, s []byte) {
|
||||
return sig[:ed25519Size], sig[ed25519Size:]
|
||||
}
|
||||
|
||||
// UnmarshalSignature decodes R and S in the native format, re-adding the stripped leading zeroes
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.1
|
||||
func (c *ed25519) UnmarshalSignature(r, s []byte) (sig []byte) {
|
||||
// Check size
|
||||
if len(r) > 32 || len(s) > 32 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sig = make([]byte, ed25519lib.SignatureSize)
|
||||
|
||||
// Handle stripped leading zeroes
|
||||
copy(sig[ed25519Size-len(r):ed25519Size], r)
|
||||
copy(sig[ed25519lib.SignatureSize-len(s):], s)
|
||||
return sig
|
||||
}
|
||||
|
||||
func (c *ed25519) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) {
|
||||
pk, sk, err := ed25519lib.GenerateKey(rand)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pk, sk[:ed25519lib.SeedSize], nil
|
||||
}
|
||||
|
||||
func getEd25519Sk(publicKey, privateKey []byte) ed25519lib.PrivateKey {
|
||||
privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey)
|
||||
|
||||
if privateKeyCap >= privateKeyLen+publicKeyLen &&
|
||||
bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) {
|
||||
return privateKey[:privateKeyLen+publicKeyLen]
|
||||
}
|
||||
|
||||
return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...)
|
||||
}
|
||||
|
||||
func (c *ed25519) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) {
|
||||
sig = ed25519lib.Sign(getEd25519Sk(publicKey, privateKey), message)
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
func (c *ed25519) Verify(publicKey, message, sig []byte) bool {
|
||||
return ed25519lib.Verify(publicKey, message, sig)
|
||||
}
|
||||
|
||||
func (c *ed25519) ValidateEdDSA(publicKey, privateKey []byte) (err error) {
|
||||
priv := getEd25519Sk(publicKey, privateKey)
|
||||
expectedPriv := ed25519lib.NewKeyFromSeed(priv.Seed())
|
||||
if subtle.ConstantTimeCompare(priv, expectedPriv) == 0 {
|
||||
return errors.KeyInvalidError("ecc: invalid ed25519 secret")
|
||||
}
|
||||
return nil
|
||||
}
|
119
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go
generated
vendored
Normal file
119
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
ed448lib "github.com/cloudflare/circl/sign/ed448"
|
||||
)
|
||||
|
||||
type ed448 struct{}
|
||||
|
||||
func NewEd448() *ed448 {
|
||||
return &ed448{}
|
||||
}
|
||||
|
||||
func (c *ed448) GetCurveName() string {
|
||||
return "ed448"
|
||||
}
|
||||
|
||||
// MarshalBytePoint encodes the public point from native format, adding the prefix.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed448) MarshalBytePoint(x []byte) []byte {
|
||||
// Return prefixed
|
||||
return append([]byte{0x40}, x...)
|
||||
}
|
||||
|
||||
// UnmarshalBytePoint decodes a point from prefixed format to native.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed448) UnmarshalBytePoint(point []byte) (x []byte) {
|
||||
if len(point) != ed448lib.PublicKeySize+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Strip prefix
|
||||
return point[1:]
|
||||
}
|
||||
|
||||
// MarshalByteSecret encoded a scalar from native format to prefixed.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed448) MarshalByteSecret(d []byte) []byte {
|
||||
// Return prefixed
|
||||
return append([]byte{0x40}, d...)
|
||||
}
|
||||
|
||||
// UnmarshalByteSecret decodes a scalar from prefixed format to native.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
|
||||
func (c *ed448) UnmarshalByteSecret(s []byte) (d []byte) {
|
||||
// Check prefixed size
|
||||
if len(s) != ed448lib.SeedSize+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Strip prefix
|
||||
return s[1:]
|
||||
}
|
||||
|
||||
// MarshalSignature splits a signature in R and S, where R is in prefixed native format and
|
||||
// S is an MPI with value zero.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2
|
||||
func (c *ed448) MarshalSignature(sig []byte) (r, s []byte) {
|
||||
return append([]byte{0x40}, sig...), []byte{}
|
||||
}
|
||||
|
||||
// UnmarshalSignature decodes R and S in the native format. Only R is used, in prefixed native format.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2
|
||||
func (c *ed448) UnmarshalSignature(r, s []byte) (sig []byte) {
|
||||
if len(r) != ed448lib.SignatureSize+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r[1:]
|
||||
}
|
||||
|
||||
func (c *ed448) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) {
|
||||
pk, sk, err := ed448lib.GenerateKey(rand)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pk, sk[:ed448lib.SeedSize], nil
|
||||
}
|
||||
|
||||
func getEd448Sk(publicKey, privateKey []byte) ed448lib.PrivateKey {
|
||||
privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey)
|
||||
|
||||
if privateKeyCap >= privateKeyLen+publicKeyLen &&
|
||||
bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) {
|
||||
return privateKey[:privateKeyLen+publicKeyLen]
|
||||
}
|
||||
|
||||
return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...)
|
||||
}
|
||||
|
||||
func (c *ed448) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) {
|
||||
// Ed448 is used with the empty string as a context string.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
|
||||
sig = ed448lib.Sign(getEd448Sk(publicKey, privateKey), message, "")
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
func (c *ed448) Verify(publicKey, message, sig []byte) bool {
|
||||
// Ed448 is used with the empty string as a context string.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
|
||||
return ed448lib.Verify(publicKey, message, sig, "")
|
||||
}
|
||||
|
||||
func (c *ed448) ValidateEdDSA(publicKey, privateKey []byte) (err error) {
|
||||
priv := getEd448Sk(publicKey, privateKey)
|
||||
expectedPriv := ed448lib.NewKeyFromSeed(priv.Seed())
|
||||
if subtle.ConstantTimeCompare(priv, expectedPriv) == 0 {
|
||||
return errors.KeyInvalidError("ecc: invalid ed448 secret")
|
||||
}
|
||||
return nil
|
||||
}
|
149
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/generic.go
generated
vendored
Normal file
149
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/generic.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type genericCurve struct {
|
||||
Curve elliptic.Curve
|
||||
}
|
||||
|
||||
func NewGenericCurve(c elliptic.Curve) *genericCurve {
|
||||
return &genericCurve{
|
||||
Curve: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *genericCurve) GetCurveName() string {
|
||||
return c.Curve.Params().Name
|
||||
}
|
||||
|
||||
func (c *genericCurve) MarshalBytePoint(point []byte) []byte {
|
||||
return point
|
||||
}
|
||||
|
||||
func (c *genericCurve) UnmarshalBytePoint(point []byte) []byte {
|
||||
return point
|
||||
}
|
||||
|
||||
func (c *genericCurve) MarshalIntegerPoint(x, y *big.Int) []byte {
|
||||
return elliptic.Marshal(c.Curve, x, y)
|
||||
}
|
||||
|
||||
func (c *genericCurve) UnmarshalIntegerPoint(point []byte) (x, y *big.Int) {
|
||||
return elliptic.Unmarshal(c.Curve, point)
|
||||
}
|
||||
|
||||
func (c *genericCurve) MarshalByteSecret(d []byte) []byte {
|
||||
return d
|
||||
}
|
||||
|
||||
func (c *genericCurve) UnmarshalByteSecret(d []byte) []byte {
|
||||
return d
|
||||
}
|
||||
|
||||
func (c *genericCurve) MarshalIntegerSecret(d *big.Int) []byte {
|
||||
return d.Bytes()
|
||||
}
|
||||
|
||||
func (c *genericCurve) UnmarshalIntegerSecret(d []byte) *big.Int {
|
||||
return new(big.Int).SetBytes(d)
|
||||
}
|
||||
|
||||
func (c *genericCurve) GenerateECDH(rand io.Reader) (point, secret []byte, err error) {
|
||||
secret, x, y, err := elliptic.GenerateKey(c.Curve, rand)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
point = elliptic.Marshal(c.Curve, x, y)
|
||||
return point, secret, nil
|
||||
}
|
||||
|
||||
func (c *genericCurve) GenerateECDSA(rand io.Reader) (x, y, secret *big.Int, err error) {
|
||||
priv, err := ecdsa.GenerateKey(c.Curve, rand)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return priv.X, priv.Y, priv.D, nil
|
||||
}
|
||||
|
||||
func (c *genericCurve) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
|
||||
xP, yP := elliptic.Unmarshal(c.Curve, point)
|
||||
if xP == nil {
|
||||
panic("invalid point")
|
||||
}
|
||||
|
||||
d, x, y, err := elliptic.GenerateKey(c.Curve, rand)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
vsG := elliptic.Marshal(c.Curve, x, y)
|
||||
zbBig, _ := c.Curve.ScalarMult(xP, yP, d)
|
||||
|
||||
byteLen := (c.Curve.Params().BitSize + 7) >> 3
|
||||
zb := make([]byte, byteLen)
|
||||
zbBytes := zbBig.Bytes()
|
||||
copy(zb[byteLen-len(zbBytes):], zbBytes)
|
||||
|
||||
return vsG, zb, nil
|
||||
}
|
||||
|
||||
func (c *genericCurve) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) {
|
||||
x, y := elliptic.Unmarshal(c.Curve, ephemeral)
|
||||
zbBig, _ := c.Curve.ScalarMult(x, y, secret)
|
||||
byteLen := (c.Curve.Params().BitSize + 7) >> 3
|
||||
zb := make([]byte, byteLen)
|
||||
zbBytes := zbBig.Bytes()
|
||||
copy(zb[byteLen-len(zbBytes):], zbBytes)
|
||||
|
||||
return zb, nil
|
||||
}
|
||||
|
||||
func (c *genericCurve) Sign(rand io.Reader, x, y, d *big.Int, hash []byte) (r, s *big.Int, err error) {
|
||||
priv := &ecdsa.PrivateKey{D: d, PublicKey: ecdsa.PublicKey{X: x, Y: y, Curve: c.Curve}}
|
||||
return ecdsa.Sign(rand, priv, hash)
|
||||
}
|
||||
|
||||
func (c *genericCurve) Verify(x, y *big.Int, hash []byte, r, s *big.Int) bool {
|
||||
pub := &ecdsa.PublicKey{X: x, Y: y, Curve: c.Curve}
|
||||
return ecdsa.Verify(pub, hash, r, s)
|
||||
}
|
||||
|
||||
func (c *genericCurve) validate(xP, yP *big.Int, secret []byte) error {
|
||||
// the public point should not be at infinity (0,0)
|
||||
zero := new(big.Int)
|
||||
if xP.Cmp(zero) == 0 && yP.Cmp(zero) == 0 {
|
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): infinity point", c.Curve.Params().Name))
|
||||
}
|
||||
|
||||
// re-derive the public point Q' = (X,Y) = dG
|
||||
// to compare to declared Q in public key
|
||||
expectedX, expectedY := c.Curve.ScalarBaseMult(secret)
|
||||
if xP.Cmp(expectedX) != 0 || yP.Cmp(expectedY) != 0 {
|
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *genericCurve) ValidateECDSA(xP, yP *big.Int, secret []byte) error {
|
||||
return c.validate(xP, yP, secret)
|
||||
}
|
||||
|
||||
func (c *genericCurve) ValidateECDH(point []byte, secret []byte) error {
|
||||
xP, yP := elliptic.Unmarshal(c.Curve, point)
|
||||
if xP == nil {
|
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name))
|
||||
}
|
||||
|
||||
return c.validate(xP, yP, secret)
|
||||
}
|
107
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/x448.go
generated
vendored
Normal file
107
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/x448.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
x448lib "github.com/cloudflare/circl/dh/x448"
|
||||
)
|
||||
|
||||
type x448 struct{}
|
||||
|
||||
func NewX448() *x448 {
|
||||
return &x448{}
|
||||
}
|
||||
|
||||
func (c *x448) GetCurveName() string {
|
||||
return "x448"
|
||||
}
|
||||
|
||||
// MarshalBytePoint encodes the public point from native format, adding the prefix.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
|
||||
func (c *x448) MarshalBytePoint(point []byte) []byte {
|
||||
return append([]byte{0x40}, point...)
|
||||
}
|
||||
|
||||
// UnmarshalBytePoint decodes a point from prefixed format to native.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
|
||||
func (c *x448) UnmarshalBytePoint(point []byte) []byte {
|
||||
if len(point) != x448lib.Size+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return point[1:]
|
||||
}
|
||||
|
||||
// MarshalByteSecret encoded a scalar from native format to prefixed.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2
|
||||
func (c *x448) MarshalByteSecret(d []byte) []byte {
|
||||
return append([]byte{0x40}, d...)
|
||||
}
|
||||
|
||||
// UnmarshalByteSecret decodes a scalar from prefixed format to native.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2
|
||||
func (c *x448) UnmarshalByteSecret(d []byte) []byte {
|
||||
if len(d) != x448lib.Size+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store without prefix
|
||||
return d[1:]
|
||||
}
|
||||
|
||||
func (c *x448) generateKeyPairBytes(rand io.Reader) (sk, pk x448lib.Key, err error) {
|
||||
if _, err = rand.Read(sk[:]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
x448lib.KeyGen(&pk, &sk)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *x448) GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error) {
|
||||
priv, pub, err := c.generateKeyPairBytes(rand)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return pub[:], priv[:], nil
|
||||
}
|
||||
|
||||
func (c *x448) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
|
||||
var pk, ss x448lib.Key
|
||||
seed, e, err := c.generateKeyPairBytes(rand)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
copy(pk[:], point)
|
||||
x448lib.Shared(&ss, &seed, &pk)
|
||||
|
||||
return e[:], ss[:], nil
|
||||
}
|
||||
|
||||
func (c *x448) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) {
|
||||
var ss, sk, e x448lib.Key
|
||||
|
||||
copy(sk[:], secret)
|
||||
copy(e[:], ephemeral)
|
||||
x448lib.Shared(&ss, &sk, &e)
|
||||
|
||||
return ss[:], nil
|
||||
}
|
||||
|
||||
func (c *x448) ValidateECDH(point []byte, secret []byte) error {
|
||||
var sk, pk, expectedPk x448lib.Key
|
||||
|
||||
copy(pk[:], point)
|
||||
copy(sk[:], secret)
|
||||
x448lib.KeyGen(&expectedPk, &sk)
|
||||
|
||||
if subtle.ConstantTimeCompare(expectedPk[:], pk[:]) == 0 {
|
||||
return errors.KeyInvalidError("ecc: invalid curve25519 public point")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
27
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go
generated
vendored
Normal file
27
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// 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)
|
||||
}
|
91
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go
generated
vendored
Normal file
91
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
// 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
|
||||
}
|
88
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go
generated
vendored
Normal file
88
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
// 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 argument 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
|
||||
}
|
456
vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
generated
vendored
Normal file
456
vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
generated
vendored
Normal file
@ -0,0 +1,456 @@
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ed25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ed448"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x448"
|
||||
)
|
||||
|
||||
// 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()
|
||||
|
||||
// Generate a primary signing key
|
||||
primaryPrivRaw, err := newSigner(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
|
||||
if config.V6() {
|
||||
if err := primary.UpgradeToV6(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
e := &Entity{
|
||||
PrimaryKey: &primary.PublicKey,
|
||||
PrivateKey: primary,
|
||||
Identities: make(map[string]*Identity),
|
||||
Subkeys: []Subkey{},
|
||||
Signatures: []*packet.Signature{},
|
||||
}
|
||||
|
||||
if config.V6() {
|
||||
// In v6 keys algorithm preferences should be stored in direct key signatures
|
||||
selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeDirectSignature, config)
|
||||
err = writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = selfSignature.SignDirectKeyBinding(&primary.PublicKey, primary, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Signatures = append(e.Signatures, selfSignature)
|
||||
e.SelfSignature = selfSignature
|
||||
}
|
||||
|
||||
err = e.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs, !config.V6())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NOTE: No key expiry here, but we will not return this subkey in EncryptionKey()
|
||||
// if the primary/master key has expired.
|
||||
err = e.addEncryptionSubkey(config, creationTime, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (t *Entity) AddUserId(name, comment, email string, config *packet.Config) error {
|
||||
creationTime := config.Now()
|
||||
keyLifetimeSecs := config.KeyLifetime()
|
||||
return t.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs, !config.V6())
|
||||
}
|
||||
|
||||
func writeKeyProperties(selfSignature *packet.Signature, creationTime time.Time, keyLifetimeSecs uint32, config *packet.Config) error {
|
||||
advertiseAead := config.AEAD() != nil
|
||||
|
||||
selfSignature.CreationTime = creationTime
|
||||
selfSignature.KeyLifetimeSecs = &keyLifetimeSecs
|
||||
selfSignature.FlagsValid = true
|
||||
selfSignature.FlagSign = true
|
||||
selfSignature.FlagCertify = true
|
||||
selfSignature.SEIPDv1 = true // true by default, see 5.8 vs. 5.14
|
||||
selfSignature.SEIPDv2 = advertiseAead
|
||||
|
||||
// Set the PreferredHash for the SelfSignature from the packet.Config.
|
||||
// If it is not the must-implement algorithm from rfc4880bis, append that.
|
||||
hash, ok := algorithm.HashToHashId(config.Hash())
|
||||
if !ok {
|
||||
return errors.UnsupportedError("unsupported preferred hash function")
|
||||
}
|
||||
|
||||
selfSignature.PreferredHash = []uint8{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))
|
||||
}
|
||||
|
||||
// We set CompressionNone as the preferred compression algorithm because
|
||||
// of compression side channel attacks, then append the configured
|
||||
// DefaultCompressionAlgo if any is set (to signal support for cases
|
||||
// where the application knows that using compression is safe).
|
||||
selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)}
|
||||
if config.Compression() != packet.CompressionNone {
|
||||
selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression()))
|
||||
}
|
||||
|
||||
if advertiseAead {
|
||||
// Get the preferred AEAD mode from the packet.Config.
|
||||
// If it is not the must-implement algorithm from rfc9580, append that.
|
||||
modes := []uint8{uint8(config.AEAD().Mode())}
|
||||
if config.AEAD().Mode() != packet.AEADModeOCB {
|
||||
modes = append(modes, uint8(packet.AEADModeOCB))
|
||||
}
|
||||
|
||||
// For preferred (AES256, GCM), we'll generate (AES256, GCM), (AES256, OCB), (AES128, GCM), (AES128, OCB)
|
||||
for _, cipher := range selfSignature.PreferredSymmetric {
|
||||
for _, mode := range modes {
|
||||
selfSignature.PreferredCipherSuites = append(selfSignature.PreferredCipherSuites, [2]uint8{cipher, mode})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Entity) addUserId(name, comment, email string, config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32, writeProperties bool) error {
|
||||
uid := packet.NewUserId(name, comment, email)
|
||||
if uid == nil {
|
||||
return errors.InvalidArgumentError("user id field contained invalid characters")
|
||||
}
|
||||
|
||||
if _, ok := t.Identities[uid.Id]; ok {
|
||||
return errors.InvalidArgumentError("user id exist")
|
||||
}
|
||||
|
||||
primary := t.PrivateKey
|
||||
isPrimaryId := len(t.Identities) == 0
|
||||
selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config)
|
||||
if writeProperties {
|
||||
err := writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
selfSignature.IsPrimaryId = &isPrimaryId
|
||||
|
||||
// User ID binding signature
|
||||
err := selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Identities[uid.Id] = &Identity{
|
||||
Name: uid.Id,
|
||||
UserId: uid,
|
||||
SelfSignature: selfSignature,
|
||||
Signatures: []*packet.Signature{selfSignature},
|
||||
}
|
||||
return 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)
|
||||
sub.IsSubkey = true
|
||||
if config.V6() {
|
||||
if err := sub.UpgradeToV6(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
subkey := Subkey{
|
||||
PublicKey: &sub.PublicKey,
|
||||
PrivateKey: sub,
|
||||
}
|
||||
subkey.Sig = createSignaturePacket(e.PrimaryKey, packet.SigTypeSubkeyBinding, config)
|
||||
subkey.Sig.CreationTime = creationTime
|
||||
subkey.Sig.KeyLifetimeSecs = &keyLifetimeSecs
|
||||
subkey.Sig.FlagsValid = true
|
||||
subkey.Sig.FlagSign = true
|
||||
subkey.Sig.EmbeddedSignature = createSignaturePacket(subkey.PublicKey, packet.SigTypePrimaryKeyBinding, config)
|
||||
subkey.Sig.EmbeddedSignature.CreationTime = creationTime
|
||||
|
||||
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
|
||||
if 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()
|
||||
return e.addEncryptionSubkey(config, creationTime, keyLifetimeSecs)
|
||||
}
|
||||
|
||||
func (e *Entity) addEncryptionSubkey(config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32) error {
|
||||
subPrivRaw, err := newDecrypter(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
|
||||
sub.IsSubkey = true
|
||||
if config.V6() {
|
||||
if err := sub.UpgradeToV6(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
subkey := Subkey{
|
||||
PublicKey: &sub.PublicKey,
|
||||
PrivateKey: sub,
|
||||
}
|
||||
subkey.Sig = createSignaturePacket(e.PrimaryKey, packet.SigTypeSubkeyBinding, config)
|
||||
subkey.Sig.CreationTime = creationTime
|
||||
subkey.Sig.KeyLifetimeSecs = &keyLifetimeSecs
|
||||
subkey.Sig.FlagsValid = true
|
||||
subkey.Sig.FlagEncryptStorage = true
|
||||
subkey.Sig.FlagEncryptCommunications = true
|
||||
|
||||
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Subkeys = append(e.Subkeys, subkey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates a signing key
|
||||
func newSigner(config *packet.Config) (signer 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:
|
||||
if config.V6() {
|
||||
// Implementations MUST NOT accept or generate v6 key material
|
||||
// using the deprecated OIDs.
|
||||
return nil, errors.InvalidArgumentError("EdDSALegacy cannot be used for v6 keys")
|
||||
}
|
||||
curve := ecc.FindEdDSAByGenName(string(config.CurveName()))
|
||||
if curve == nil {
|
||||
return nil, errors.InvalidArgumentError("unsupported curve")
|
||||
}
|
||||
|
||||
priv, err := eddsa.GenerateKey(config.Random(), curve)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return priv, nil
|
||||
case packet.PubKeyAlgoECDSA:
|
||||
curve := ecc.FindECDSAByGenName(string(config.CurveName()))
|
||||
if curve == nil {
|
||||
return nil, errors.InvalidArgumentError("unsupported curve")
|
||||
}
|
||||
|
||||
priv, err := ecdsa.GenerateKey(config.Random(), curve)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return priv, nil
|
||||
case packet.PubKeyAlgoEd25519:
|
||||
priv, err := ed25519.GenerateKey(config.Random())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return priv, nil
|
||||
case packet.PubKeyAlgoEd448:
|
||||
priv, err := ed448.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, packet.PubKeyAlgoECDSA:
|
||||
fallthrough // When passing EdDSA or ECDSA, we generate an ECDH subkey
|
||||
case packet.PubKeyAlgoECDH:
|
||||
if config.V6() &&
|
||||
(config.CurveName() == packet.Curve25519 ||
|
||||
config.CurveName() == packet.Curve448) {
|
||||
// Implementations MUST NOT accept or generate v6 key material
|
||||
// using the deprecated OIDs.
|
||||
return nil, errors.InvalidArgumentError("ECDH with Curve25519/448 legacy cannot be used for v6 keys")
|
||||
}
|
||||
var kdf = ecdh.KDF{
|
||||
Hash: algorithm.SHA512,
|
||||
Cipher: algorithm.AES256,
|
||||
}
|
||||
curve := ecc.FindECDHByGenName(string(config.CurveName()))
|
||||
if curve == nil {
|
||||
return nil, errors.InvalidArgumentError("unsupported curve")
|
||||
}
|
||||
return ecdh.GenerateKey(config.Random(), curve, kdf)
|
||||
case packet.PubKeyAlgoEd25519, packet.PubKeyAlgoX25519: // When passing Ed25519, we generate an x25519 subkey
|
||||
return x25519.GenerateKey(config.Random())
|
||||
case packet.PubKeyAlgoEd448, packet.PubKeyAlgoX448: // When passing Ed448, we generate an x448 subkey
|
||||
return x448.GenerateKey(config.Random())
|
||||
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 pre-populated 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
|
||||
}
|
901
vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
generated
vendored
Normal file
901
vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
generated
vendored
Normal file
@ -0,0 +1,901 @@
|
||||
// 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 (
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// PublicKeyType is the armor type for a PGP public key.
|
||||
var PublicKeyType = "PGP PUBLIC KEY BLOCK"
|
||||
|
||||
// PrivateKeyType is the armor type for a PGP private key.
|
||||
var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
|
||||
|
||||
// An Entity represents the components of an OpenPGP key: a primary public key
|
||||
// (which must be a signing key), one or more identities claimed by that key,
|
||||
// and zero or more subkeys, which may be encryption keys.
|
||||
type Entity struct {
|
||||
PrimaryKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Identities map[string]*Identity // indexed by Identity.Name
|
||||
Revocations []*packet.Signature
|
||||
Subkeys []Subkey
|
||||
SelfSignature *packet.Signature // Direct-key self signature of the PrimaryKey (contains primary key properties in v6)
|
||||
Signatures []*packet.Signature // all (potentially unverified) self-signatures, revocations, and third-party signatures
|
||||
}
|
||||
|
||||
// An Identity represents an identity claimed by an Entity and zero or more
|
||||
// assertions by other entities about that claim.
|
||||
type Identity struct {
|
||||
Name string // by convention, has the form "Full Name (comment) <email@example.com>"
|
||||
UserId *packet.UserId
|
||||
SelfSignature *packet.Signature
|
||||
Revocations []*packet.Signature
|
||||
Signatures []*packet.Signature // all (potentially unverified) self-signatures, revocations, and third-party signatures
|
||||
}
|
||||
|
||||
// A Subkey is an additional public key in an Entity. Subkeys can be used for
|
||||
// encryption.
|
||||
type Subkey struct {
|
||||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Sig *packet.Signature
|
||||
Revocations []*packet.Signature
|
||||
}
|
||||
|
||||
// A Key identifies a specific public key in an Entity. This is either the
|
||||
// Entity's primary key or a subkey.
|
||||
type Key struct {
|
||||
Entity *Entity
|
||||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
SelfSignature *packet.Signature
|
||||
Revocations []*packet.Signature
|
||||
}
|
||||
|
||||
// A KeyRing provides access to public and private keys.
|
||||
type KeyRing interface {
|
||||
// KeysById returns the set of keys that have the given key id.
|
||||
KeysById(id uint64) []Key
|
||||
// KeysByIdAndUsage returns the set of keys with the given id
|
||||
// that also meet the key usage given by requiredUsage.
|
||||
// The requiredUsage is expressed as the bitwise-OR of
|
||||
// packet.KeyFlag* values.
|
||||
KeysByIdUsage(id uint64, requiredUsage byte) []Key
|
||||
// DecryptionKeys returns all private keys that are valid for
|
||||
// decryption.
|
||||
DecryptionKeys() []Key
|
||||
}
|
||||
|
||||
// PrimaryIdentity returns an Identity, preferring non-revoked identities,
|
||||
// identities marked as primary, or the latest-created identity, in that order.
|
||||
func (e *Entity) PrimaryIdentity() *Identity {
|
||||
var primaryIdentity *Identity
|
||||
for _, ident := range e.Identities {
|
||||
if shouldPreferIdentity(primaryIdentity, ident) {
|
||||
primaryIdentity = ident
|
||||
}
|
||||
}
|
||||
return primaryIdentity
|
||||
}
|
||||
|
||||
func shouldPreferIdentity(existingId, potentialNewId *Identity) bool {
|
||||
if existingId == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(existingId.Revocations) > len(potentialNewId.Revocations) {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(existingId.Revocations) < len(potentialNewId.Revocations) {
|
||||
return false
|
||||
}
|
||||
|
||||
if existingId.SelfSignature == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if existingId.SelfSignature.IsPrimaryId != nil && *existingId.SelfSignature.IsPrimaryId &&
|
||||
!(potentialNewId.SelfSignature.IsPrimaryId != nil && *potentialNewId.SelfSignature.IsPrimaryId) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !(existingId.SelfSignature.IsPrimaryId != nil && *existingId.SelfSignature.IsPrimaryId) &&
|
||||
potentialNewId.SelfSignature.IsPrimaryId != nil && *potentialNewId.SelfSignature.IsPrimaryId {
|
||||
return true
|
||||
}
|
||||
|
||||
return potentialNewId.SelfSignature.CreationTime.After(existingId.SelfSignature.CreationTime)
|
||||
}
|
||||
|
||||
// EncryptionKey returns the best candidate Key for encrypting a message to the
|
||||
// given Entity.
|
||||
func (e *Entity) EncryptionKey(now time.Time) (Key, bool) {
|
||||
// Fail to find any encryption key if the...
|
||||
primarySelfSignature, primaryIdentity := e.PrimarySelfSignature()
|
||||
if primarySelfSignature == nil || // no self-signature found
|
||||
e.PrimaryKey.KeyExpired(primarySelfSignature, now) || // primary key has expired
|
||||
e.Revoked(now) || // primary key has been revoked
|
||||
primarySelfSignature.SigExpired(now) || // user ID or or direct self-signature has expired
|
||||
(primaryIdentity != nil && primaryIdentity.Revoked(now)) { // user ID has been revoked (for v4 keys)
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// Iterate the keys to find the newest, unexpired one
|
||||
candidateSubkey := -1
|
||||
var maxTime time.Time
|
||||
for i, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagEncryptCommunications &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanEncrypt() &&
|
||||
!subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
|
||||
!subkey.Sig.SigExpired(now) &&
|
||||
!subkey.Revoked(now) &&
|
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
|
||||
candidateSubkey = i
|
||||
maxTime = subkey.Sig.CreationTime
|
||||
}
|
||||
}
|
||||
|
||||
if candidateSubkey != -1 {
|
||||
subkey := e.Subkeys[candidateSubkey]
|
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig, subkey.Revocations}, true
|
||||
}
|
||||
|
||||
// If we don't have any subkeys for encryption and the primary key
|
||||
// is marked as OK to encrypt with, then we can use it.
|
||||
if primarySelfSignature.FlagsValid && primarySelfSignature.FlagEncryptCommunications &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanEncrypt() {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, primarySelfSignature, e.Revocations}, true
|
||||
}
|
||||
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// CertificationKey return the best candidate Key for certifying a key with this
|
||||
// Entity.
|
||||
func (e *Entity) CertificationKey(now time.Time) (Key, bool) {
|
||||
return e.CertificationKeyById(now, 0)
|
||||
}
|
||||
|
||||
// CertificationKeyById return the Key for key certification with this
|
||||
// Entity and keyID.
|
||||
func (e *Entity) CertificationKeyById(now time.Time, id uint64) (Key, bool) {
|
||||
return e.signingKeyByIdUsage(now, id, packet.KeyFlagCertify)
|
||||
}
|
||||
|
||||
// SigningKey return the best candidate Key for signing a message with this
|
||||
// Entity.
|
||||
func (e *Entity) SigningKey(now time.Time) (Key, bool) {
|
||||
return e.SigningKeyById(now, 0)
|
||||
}
|
||||
|
||||
// SigningKeyById return the Key for signing a message with this
|
||||
// Entity and keyID.
|
||||
func (e *Entity) SigningKeyById(now time.Time, id uint64) (Key, bool) {
|
||||
return e.signingKeyByIdUsage(now, id, packet.KeyFlagSign)
|
||||
}
|
||||
|
||||
func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key, bool) {
|
||||
// Fail to find any signing key if the...
|
||||
primarySelfSignature, primaryIdentity := e.PrimarySelfSignature()
|
||||
if primarySelfSignature == nil || // no self-signature found
|
||||
e.PrimaryKey.KeyExpired(primarySelfSignature, now) || // primary key has expired
|
||||
e.Revoked(now) || // primary key has been revoked
|
||||
primarySelfSignature.SigExpired(now) || // user ID or direct self-signature has expired
|
||||
(primaryIdentity != nil && primaryIdentity.Revoked(now)) { // user ID has been revoked (for v4 keys)
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// Iterate the keys to find the newest, unexpired one
|
||||
candidateSubkey := -1
|
||||
var maxTime time.Time
|
||||
for idx, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
(flags&packet.KeyFlagCertify == 0 || subkey.Sig.FlagCertify) &&
|
||||
(flags&packet.KeyFlagSign == 0 || subkey.Sig.FlagSign) &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanSign() &&
|
||||
!subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
|
||||
!subkey.Sig.SigExpired(now) &&
|
||||
!subkey.Revoked(now) &&
|
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) &&
|
||||
(id == 0 || subkey.PublicKey.KeyId == id) {
|
||||
candidateSubkey = idx
|
||||
maxTime = subkey.Sig.CreationTime
|
||||
}
|
||||
}
|
||||
|
||||
if candidateSubkey != -1 {
|
||||
subkey := e.Subkeys[candidateSubkey]
|
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig, subkey.Revocations}, true
|
||||
}
|
||||
|
||||
// If we don't have any subkeys for signing and the primary key
|
||||
// is marked as OK to sign with, then we can use it.
|
||||
if primarySelfSignature.FlagsValid &&
|
||||
(flags&packet.KeyFlagCertify == 0 || primarySelfSignature.FlagCertify) &&
|
||||
(flags&packet.KeyFlagSign == 0 || primarySelfSignature.FlagSign) &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanSign() &&
|
||||
(id == 0 || e.PrimaryKey.KeyId == id) {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, primarySelfSignature, e.Revocations}, true
|
||||
}
|
||||
|
||||
// No keys with a valid Signing Flag or no keys matched the id passed in
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
func revoked(revocations []*packet.Signature, now time.Time) bool {
|
||||
for _, revocation := range revocations {
|
||||
if revocation.RevocationReason != nil && *revocation.RevocationReason == packet.KeyCompromised {
|
||||
// If the key is compromised, the key is considered revoked even before the revocation date.
|
||||
return true
|
||||
}
|
||||
if !revocation.SigExpired(now) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Revoked returns whether the entity has any direct key revocation signatures.
|
||||
// Note that third-party revocation signatures are not supported.
|
||||
// Note also that Identity and Subkey revocation should be checked separately.
|
||||
func (e *Entity) Revoked(now time.Time) bool {
|
||||
return revoked(e.Revocations, now)
|
||||
}
|
||||
|
||||
// EncryptPrivateKeys encrypts all non-encrypted keys in the entity with the same key
|
||||
// derived from the provided passphrase. Public keys and dummy keys are ignored,
|
||||
// and don't cause an error to be returned.
|
||||
func (e *Entity) EncryptPrivateKeys(passphrase []byte, config *packet.Config) error {
|
||||
var keysToEncrypt []*packet.PrivateKey
|
||||
// Add entity private key to encrypt.
|
||||
if e.PrivateKey != nil && !e.PrivateKey.Dummy() && !e.PrivateKey.Encrypted {
|
||||
keysToEncrypt = append(keysToEncrypt, e.PrivateKey)
|
||||
}
|
||||
|
||||
// Add subkeys to encrypt.
|
||||
for _, sub := range e.Subkeys {
|
||||
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() && !sub.PrivateKey.Encrypted {
|
||||
keysToEncrypt = append(keysToEncrypt, sub.PrivateKey)
|
||||
}
|
||||
}
|
||||
return packet.EncryptPrivateKeys(keysToEncrypt, passphrase, config)
|
||||
}
|
||||
|
||||
// DecryptPrivateKeys decrypts all encrypted keys in the entity with the given passphrase.
|
||||
// Avoids recomputation of similar s2k key derivations. Public keys and dummy keys are ignored,
|
||||
// and don't cause an error to be returned.
|
||||
func (e *Entity) DecryptPrivateKeys(passphrase []byte) error {
|
||||
var keysToDecrypt []*packet.PrivateKey
|
||||
// Add entity private key to decrypt.
|
||||
if e.PrivateKey != nil && !e.PrivateKey.Dummy() && e.PrivateKey.Encrypted {
|
||||
keysToDecrypt = append(keysToDecrypt, e.PrivateKey)
|
||||
}
|
||||
|
||||
// Add subkeys to decrypt.
|
||||
for _, sub := range e.Subkeys {
|
||||
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() && sub.PrivateKey.Encrypted {
|
||||
keysToDecrypt = append(keysToDecrypt, sub.PrivateKey)
|
||||
}
|
||||
}
|
||||
return packet.DecryptPrivateKeys(keysToDecrypt, passphrase)
|
||||
}
|
||||
|
||||
// Revoked returns whether the identity has been revoked by a self-signature.
|
||||
// Note that third-party revocation signatures are not supported.
|
||||
func (i *Identity) Revoked(now time.Time) bool {
|
||||
return revoked(i.Revocations, now)
|
||||
}
|
||||
|
||||
// Revoked returns whether the subkey has been revoked by a self-signature.
|
||||
// Note that third-party revocation signatures are not supported.
|
||||
func (s *Subkey) Revoked(now time.Time) bool {
|
||||
return revoked(s.Revocations, now)
|
||||
}
|
||||
|
||||
// Revoked returns whether the key or subkey has been revoked by a self-signature.
|
||||
// Note that third-party revocation signatures are not supported.
|
||||
// Note also that Identity revocation should be checked separately.
|
||||
// Normally, it's not necessary to call this function, except on keys returned by
|
||||
// KeysById or KeysByIdUsage.
|
||||
func (key *Key) Revoked(now time.Time) bool {
|
||||
return revoked(key.Revocations, now)
|
||||
}
|
||||
|
||||
// An EntityList contains one or more Entities.
|
||||
type EntityList []*Entity
|
||||
|
||||
// KeysById returns the set of keys that have the given key id.
|
||||
func (el EntityList) KeysById(id uint64) (keys []Key) {
|
||||
for _, e := range el {
|
||||
if e.PrimaryKey.KeyId == id {
|
||||
selfSig, _ := e.PrimarySelfSignature()
|
||||
keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig, e.Revocations})
|
||||
}
|
||||
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PublicKey.KeyId == id {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig, subKey.Revocations})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KeysByIdAndUsage returns the set of keys with the given id that also meet
|
||||
// the key usage given by requiredUsage. The requiredUsage is expressed as
|
||||
// the bitwise-OR of packet.KeyFlag* values.
|
||||
func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {
|
||||
for _, key := range el.KeysById(id) {
|
||||
if requiredUsage != 0 {
|
||||
if key.SelfSignature == nil || !key.SelfSignature.FlagsValid {
|
||||
continue
|
||||
}
|
||||
|
||||
var usage byte
|
||||
if key.SelfSignature.FlagCertify {
|
||||
usage |= packet.KeyFlagCertify
|
||||
}
|
||||
if key.SelfSignature.FlagSign {
|
||||
usage |= packet.KeyFlagSign
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptCommunications {
|
||||
usage |= packet.KeyFlagEncryptCommunications
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptStorage {
|
||||
usage |= packet.KeyFlagEncryptStorage
|
||||
}
|
||||
if usage&requiredUsage != requiredUsage {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptionKeys returns all private keys that are valid for decryption.
|
||||
func (el EntityList) DecryptionKeys() (keys []Key) {
|
||||
for _, e := range el {
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PrivateKey != nil && subKey.Sig.FlagsValid && (subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig, subKey.Revocations})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file.
|
||||
func ReadArmoredKeyRing(r io.Reader) (EntityList, error) {
|
||||
block, err := armor.Decode(r)
|
||||
if err == io.EOF {
|
||||
return nil, errors.InvalidArgumentError("no armored data found")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if block.Type != PublicKeyType && block.Type != PrivateKeyType {
|
||||
return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type)
|
||||
}
|
||||
|
||||
return ReadKeyRing(block.Body)
|
||||
}
|
||||
|
||||
// ReadKeyRing reads one or more public/private keys. Unsupported keys are
|
||||
// ignored as long as at least a single valid key is found.
|
||||
func ReadKeyRing(r io.Reader) (el EntityList, err error) {
|
||||
packets := packet.NewReader(r)
|
||||
var lastUnsupportedError error
|
||||
|
||||
for {
|
||||
var e *Entity
|
||||
e, err = ReadEntity(packets)
|
||||
if err != nil {
|
||||
// TODO: warn about skipped unsupported/unreadable keys
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
lastUnsupportedError = err
|
||||
err = readToNextPublicKey(packets)
|
||||
} else if _, ok := err.(errors.StructuralError); ok {
|
||||
// Skip unreadable, badly-formatted keys
|
||||
lastUnsupportedError = err
|
||||
err = readToNextPublicKey(packets)
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
el = nil
|
||||
break
|
||||
}
|
||||
} else {
|
||||
el = append(el, e)
|
||||
}
|
||||
}
|
||||
|
||||
if len(el) == 0 && err == nil {
|
||||
err = lastUnsupportedError
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readToNextPublicKey reads packets until the start of the entity and leaves
|
||||
// the first packet of the new entity in the Reader.
|
||||
func readToNextPublicKey(packets *packet.Reader) (err error) {
|
||||
var p packet.Packet
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey {
|
||||
packets.Unread(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEntity reads an entity (public key, identities, subkeys etc) from the
|
||||
// given Reader.
|
||||
func ReadEntity(packets *packet.Reader) (*Entity, error) {
|
||||
e := new(Entity)
|
||||
e.Identities = make(map[string]*Identity)
|
||||
|
||||
p, err := packets.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok {
|
||||
if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok {
|
||||
packets.Unread(p)
|
||||
return nil, errors.StructuralError("first packet was not a public/private key")
|
||||
}
|
||||
e.PrimaryKey = &e.PrivateKey.PublicKey
|
||||
}
|
||||
|
||||
if !e.PrimaryKey.PubKeyAlgo.CanSign() {
|
||||
return nil, errors.StructuralError("primary key cannot be used for signatures")
|
||||
}
|
||||
|
||||
var revocations []*packet.Signature
|
||||
var directSignatures []*packet.Signature
|
||||
EachPacket:
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pkt := p.(type) {
|
||||
case *packet.UserId:
|
||||
if err := addUserID(e, packets, pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *packet.Signature:
|
||||
if pkt.SigType == packet.SigTypeKeyRevocation {
|
||||
revocations = append(revocations, pkt)
|
||||
} else if pkt.SigType == packet.SigTypeDirectSignature {
|
||||
directSignatures = append(directSignatures, pkt)
|
||||
}
|
||||
// Else, ignoring the signature as it does not follow anything
|
||||
// we would know to attach it to.
|
||||
case *packet.PrivateKey:
|
||||
if !pkt.IsSubkey {
|
||||
packets.Unread(p)
|
||||
break EachPacket
|
||||
}
|
||||
err = addSubkey(e, packets, &pkt.PublicKey, pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *packet.PublicKey:
|
||||
if !pkt.IsSubkey {
|
||||
packets.Unread(p)
|
||||
break EachPacket
|
||||
}
|
||||
err = addSubkey(e, packets, pkt, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
// we ignore unknown packets.
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.Identities) == 0 && e.PrimaryKey.Version < 6 {
|
||||
return nil, errors.StructuralError(fmt.Sprintf("v%d entity without any identities", e.PrimaryKey.Version))
|
||||
}
|
||||
|
||||
// An implementation MUST ensure that a valid direct-key signature is present before using a v6 key.
|
||||
if e.PrimaryKey.Version == 6 {
|
||||
if len(directSignatures) == 0 {
|
||||
return nil, errors.StructuralError("v6 entity without a valid direct-key signature")
|
||||
}
|
||||
// Select main direct key signature.
|
||||
var mainDirectKeySelfSignature *packet.Signature
|
||||
for _, directSignature := range directSignatures {
|
||||
if directSignature.SigType == packet.SigTypeDirectSignature &&
|
||||
directSignature.CheckKeyIdOrFingerprint(e.PrimaryKey) &&
|
||||
(mainDirectKeySelfSignature == nil ||
|
||||
directSignature.CreationTime.After(mainDirectKeySelfSignature.CreationTime)) {
|
||||
mainDirectKeySelfSignature = directSignature
|
||||
}
|
||||
}
|
||||
if mainDirectKeySelfSignature == nil {
|
||||
return nil, errors.StructuralError("no valid direct-key self-signature for v6 primary key found")
|
||||
}
|
||||
// Check that the main self-signature is valid.
|
||||
err = e.PrimaryKey.VerifyDirectKeySignature(mainDirectKeySelfSignature)
|
||||
if err != nil {
|
||||
return nil, errors.StructuralError("invalid direct-key self-signature for v6 primary key")
|
||||
}
|
||||
e.SelfSignature = mainDirectKeySelfSignature
|
||||
e.Signatures = directSignatures
|
||||
}
|
||||
|
||||
for _, revocation := range revocations {
|
||||
err = e.PrimaryKey.VerifyRevocationSignature(revocation)
|
||||
if err == nil {
|
||||
e.Revocations = append(e.Revocations, revocation)
|
||||
} else {
|
||||
// TODO: RFC 4880 5.2.3.15 defines revocation keys.
|
||||
return nil, errors.StructuralError("revocation signature signed by alternate key")
|
||||
}
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error {
|
||||
// Make a new Identity object, that we might wind up throwing away.
|
||||
// We'll only add it if we get a valid self-signature over this
|
||||
// userID.
|
||||
identity := new(Identity)
|
||||
identity.Name = pkt.Id
|
||||
identity.UserId = pkt
|
||||
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
packets.Unread(p)
|
||||
break
|
||||
}
|
||||
|
||||
if sig.SigType != packet.SigTypeGenericCert &&
|
||||
sig.SigType != packet.SigTypePersonaCert &&
|
||||
sig.SigType != packet.SigTypeCasualCert &&
|
||||
sig.SigType != packet.SigTypePositiveCert &&
|
||||
sig.SigType != packet.SigTypeCertificationRevocation {
|
||||
return errors.StructuralError("user ID signature with wrong type")
|
||||
}
|
||||
|
||||
if sig.CheckKeyIdOrFingerprint(e.PrimaryKey) {
|
||||
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
|
||||
return errors.StructuralError("user ID self-signature invalid: " + err.Error())
|
||||
}
|
||||
if sig.SigType == packet.SigTypeCertificationRevocation {
|
||||
identity.Revocations = append(identity.Revocations, sig)
|
||||
} else if identity.SelfSignature == nil || sig.CreationTime.After(identity.SelfSignature.CreationTime) {
|
||||
identity.SelfSignature = sig
|
||||
}
|
||||
identity.Signatures = append(identity.Signatures, sig)
|
||||
e.Identities[pkt.Id] = identity
|
||||
} else {
|
||||
identity.Signatures = append(identity.Signatures, sig)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
|
||||
var subKey Subkey
|
||||
subKey.PublicKey = pub
|
||||
subKey.PrivateKey = priv
|
||||
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
packets.Unread(p)
|
||||
break
|
||||
}
|
||||
|
||||
if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||
return errors.StructuralError("subkey signature with wrong type")
|
||||
}
|
||||
|
||||
if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
switch sig.SigType {
|
||||
case packet.SigTypeSubkeyRevocation:
|
||||
subKey.Revocations = append(subKey.Revocations, sig)
|
||||
case packet.SigTypeSubkeyBinding:
|
||||
if subKey.Sig == nil || sig.CreationTime.After(subKey.Sig.CreationTime) {
|
||||
subKey.Sig = sig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if subKey.Sig == nil {
|
||||
return errors.StructuralError("subkey packet not followed by signature")
|
||||
}
|
||||
|
||||
e.Subkeys = append(e.Subkeys, subKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializePrivate serializes an Entity, including private key material, but
|
||||
// excluding signatures from other entities, to the given Writer.
|
||||
// Identities and subkeys are re-signed in case they changed since NewEntry.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
|
||||
if e.PrivateKey.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy private key cannot re-sign identities")
|
||||
}
|
||||
return e.serializePrivate(w, config, true)
|
||||
}
|
||||
|
||||
// SerializePrivateWithoutSigning serializes an Entity, including private key
|
||||
// material, but excluding signatures from other entities, to the given Writer.
|
||||
// Self-signatures of identities and subkeys are not re-signed. This is useful
|
||||
// when serializing GNU dummy keys, among other things.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SerializePrivateWithoutSigning(w io.Writer, config *packet.Config) (err error) {
|
||||
return e.serializePrivate(w, config, false)
|
||||
}
|
||||
|
||||
func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign bool) (err error) {
|
||||
if e.PrivateKey == nil {
|
||||
return goerrors.New("openpgp: private key is missing")
|
||||
}
|
||||
err = e.PrivateKey.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, revocation := range e.Revocations {
|
||||
err := revocation.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, directSignature := range e.Signatures {
|
||||
err := directSignature.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, ident := range e.Identities {
|
||||
err = ident.UserId.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reSign {
|
||||
if ident.SelfSignature == nil {
|
||||
return goerrors.New("openpgp: can't re-sign identity without valid self-signature")
|
||||
}
|
||||
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, sig := range ident.Signatures {
|
||||
err = sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, subkey := range e.Subkeys {
|
||||
err = subkey.PrivateKey.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reSign {
|
||||
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if subkey.Sig.EmbeddedSignature != nil {
|
||||
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey,
|
||||
subkey.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, revocation := range subkey.Revocations {
|
||||
err := revocation.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the public part of the given Entity to w, including
|
||||
// signatures from other entities. No private key material will be output.
|
||||
func (e *Entity) Serialize(w io.Writer) error {
|
||||
err := e.PrimaryKey.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, revocation := range e.Revocations {
|
||||
err := revocation.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, directSignature := range e.Signatures {
|
||||
err := directSignature.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, ident := range e.Identities {
|
||||
err = ident.UserId.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sig := range ident.Signatures {
|
||||
err = sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, subkey := range e.Subkeys {
|
||||
err = subkey.PublicKey.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, revocation := range subkey.Revocations {
|
||||
err := revocation.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignIdentity adds a signature to e, from signer, attesting that identity is
|
||||
// associated with e. The provided identity must already be an element of
|
||||
// e.Identities and the private key of signer must have been decrypted if
|
||||
// necessary.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error {
|
||||
certificationKey, ok := signer.CertificationKey(config.Now())
|
||||
if !ok {
|
||||
return errors.InvalidArgumentError("no valid certification key found")
|
||||
}
|
||||
|
||||
if certificationKey.PrivateKey.Encrypted {
|
||||
return errors.InvalidArgumentError("signing Entity's private key must be decrypted")
|
||||
}
|
||||
|
||||
ident, ok := e.Identities[identity]
|
||||
if !ok {
|
||||
return errors.InvalidArgumentError("given identity string not found in Entity")
|
||||
}
|
||||
|
||||
sig := createSignaturePacket(certificationKey.PublicKey, packet.SigTypeGenericCert, config)
|
||||
|
||||
signingUserID := config.SigningUserId()
|
||||
if signingUserID != "" {
|
||||
if _, ok := signer.Identities[signingUserID]; !ok {
|
||||
return errors.InvalidArgumentError("signer identity string not found in signer Entity")
|
||||
}
|
||||
sig.SignerUserId = &signingUserID
|
||||
}
|
||||
|
||||
if err := sig.SignUserId(identity, e.PrimaryKey, certificationKey.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
ident.Signatures = append(ident.Signatures, sig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeKey generates a key revocation signature (packet.SigTypeKeyRevocation) with the
|
||||
// specified reason code and text (RFC4880 section-5.2.3.23).
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) RevokeKey(reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
|
||||
revSig := createSignaturePacket(e.PrimaryKey, packet.SigTypeKeyRevocation, config)
|
||||
revSig.RevocationReason = &reason
|
||||
revSig.RevocationReasonText = reasonText
|
||||
|
||||
if err := revSig.RevokeKey(e.PrimaryKey, e.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Revocations = append(e.Revocations, revSig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeSubkey generates a subkey revocation signature (packet.SigTypeSubkeyRevocation) for
|
||||
// a subkey with the specified reason code and text (RFC4880 section-5.2.3.23).
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
|
||||
if err := e.PrimaryKey.VerifyKeySignature(sk.PublicKey, sk.Sig); err != nil {
|
||||
return errors.InvalidArgumentError("given subkey is not associated with this key")
|
||||
}
|
||||
|
||||
revSig := createSignaturePacket(e.PrimaryKey, packet.SigTypeSubkeyRevocation, config)
|
||||
revSig.RevocationReason = &reason
|
||||
revSig.RevocationReasonText = reasonText
|
||||
|
||||
if err := revSig.RevokeSubkey(sk.PublicKey, e.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sk.Revocations = append(sk.Revocations, revSig)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Entity) primaryDirectSignature() *packet.Signature {
|
||||
return e.SelfSignature
|
||||
}
|
||||
|
||||
// PrimarySelfSignature searches the entity for the self-signature that stores key preferences.
|
||||
// For V4 keys, returns the self-signature of the primary identity, and the identity.
|
||||
// For V6 keys, returns the latest valid direct-key self-signature, and no identity (nil).
|
||||
// This self-signature is to be used to check the key expiration,
|
||||
// algorithm preferences, and so on.
|
||||
func (e *Entity) PrimarySelfSignature() (*packet.Signature, *Identity) {
|
||||
if e.PrimaryKey.Version == 6 {
|
||||
return e.primaryDirectSignature(), nil
|
||||
}
|
||||
primaryIdentity := e.PrimaryIdentity()
|
||||
if primaryIdentity == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return primaryIdentity.SelfSignature, primaryIdentity
|
||||
}
|
538
vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go
generated
vendored
Normal file
538
vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
67
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go
generated
vendored
Normal file
67
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// CipherSuite contains a combination of Cipher and Mode
|
||||
type CipherSuite struct {
|
||||
// The cipher function
|
||||
Cipher CipherFunction
|
||||
// The AEAD mode of operation.
|
||||
Mode AEADMode
|
||||
}
|
||||
|
||||
// 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 no preference is specified, OCB is used (which is mandatory to implement).
|
||||
if conf == nil || conf.DefaultMode == 0 {
|
||||
return AEADModeOCB
|
||||
}
|
||||
|
||||
mode := conf.DefaultMode
|
||||
if mode != AEADModeEAX && mode != AEADModeOCB && mode != AEADModeGCM {
|
||||
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)
|
||||
// limit to 16 = 4 MiB
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
|
||||
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 > 16:
|
||||
exponent = 16
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
250
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
generated
vendored
Normal file
250
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// aeadCrypter is an AEAD opener/sealer, its configuration, and data for en/decryption.
|
||||
type aeadCrypter struct {
|
||||
aead cipher.AEAD
|
||||
chunkSize int
|
||||
nonce []byte
|
||||
associatedData []byte // Chunk-independent associated data
|
||||
chunkIndex []byte // Chunk counter
|
||||
packetTag packetType // SEIP packet (v2) or AEAD Encrypted Data packet
|
||||
bytesProcessed int // Amount of plaintext bytes encrypted/decrypted
|
||||
}
|
||||
|
||||
// computeNonce takes the incremental index and computes an eXclusive OR with
|
||||
// the least significant 8 bytes of the receivers' initial nonce (see sec.
|
||||
// 5.16.1 and 5.16.2). It returns the resulting nonce.
|
||||
func (wo *aeadCrypter) computeNextNonce() (nonce []byte) {
|
||||
if wo.packetTag == packetTypeSymmetricallyEncryptedIntegrityProtected {
|
||||
return wo.nonce
|
||||
}
|
||||
|
||||
nonce = make([]byte, len(wo.nonce))
|
||||
copy(nonce, wo.nonce)
|
||||
offset := len(wo.nonce) - 8
|
||||
for i := 0; i < 8; i++ {
|
||||
nonce[i+offset] ^= wo.chunkIndex[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// incrementIndex performs an integer increment by 1 of the integer represented by the
|
||||
// slice, modifying it accordingly.
|
||||
func (wo *aeadCrypter) incrementIndex() error {
|
||||
index := wo.chunkIndex
|
||||
if len(index) == 0 {
|
||||
return errors.AEADError("Index has length 0")
|
||||
}
|
||||
for i := len(index) - 1; i >= 0; i-- {
|
||||
if index[i] < 255 {
|
||||
index[i]++
|
||||
return nil
|
||||
}
|
||||
index[i] = 0
|
||||
}
|
||||
return errors.AEADError("cannot further increment index")
|
||||
}
|
||||
|
||||
// aeadDecrypter reads and decrypts bytes. It buffers extra decrypted bytes when
|
||||
// necessary, similar to aeadEncrypter.
|
||||
type aeadDecrypter struct {
|
||||
aeadCrypter // Embedded ciphertext opener
|
||||
reader io.Reader // 'reader' is a partialLengthReader
|
||||
chunkBytes []byte
|
||||
peekedBytes []byte // Used to detect last chunk
|
||||
buffer []byte // Buffered decrypted bytes
|
||||
}
|
||||
|
||||
// Read decrypts bytes and reads them into dst. It decrypts when necessary and
|
||||
// buffers extra decrypted bytes. It returns the number of bytes copied into dst
|
||||
// and an error.
|
||||
func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
|
||||
// Return buffered plaintext bytes from previous calls
|
||||
if len(ar.buffer) > 0 {
|
||||
n = copy(dst, ar.buffer)
|
||||
ar.buffer = ar.buffer[n:]
|
||||
return
|
||||
}
|
||||
|
||||
// Read a chunk
|
||||
tagLen := ar.aead.Overhead()
|
||||
copy(ar.chunkBytes, ar.peekedBytes) // Copy bytes peeked in previous chunk or in initialization
|
||||
bytesRead, errRead := io.ReadFull(ar.reader, ar.chunkBytes[tagLen:])
|
||||
if errRead != nil && errRead != io.EOF && errRead != io.ErrUnexpectedEOF {
|
||||
return 0, errRead
|
||||
}
|
||||
|
||||
if bytesRead > 0 {
|
||||
ar.peekedBytes = ar.chunkBytes[bytesRead:bytesRead+tagLen]
|
||||
|
||||
decrypted, errChunk := ar.openChunk(ar.chunkBytes[:bytesRead])
|
||||
if errChunk != nil {
|
||||
return 0, errChunk
|
||||
}
|
||||
|
||||
// Return decrypted bytes, buffering if necessary
|
||||
n = copy(dst, decrypted)
|
||||
ar.buffer = decrypted[n:]
|
||||
return
|
||||
}
|
||||
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// Close checks the final authentication tag of the stream.
|
||||
// In the future, this function could also be used to wipe the reader
|
||||
// and peeked & decrypted bytes, if necessary.
|
||||
func (ar *aeadDecrypter) Close() (err error) {
|
||||
errChunk := ar.validateFinalTag(ar.peekedBytes)
|
||||
if errChunk != nil {
|
||||
return errChunk
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// openChunk decrypts and checks integrity of an encrypted chunk, returning
|
||||
// the underlying plaintext and an error. It accesses peeked bytes from next
|
||||
// chunk, to identify the last chunk and decrypt/validate accordingly.
|
||||
func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) {
|
||||
adata := ar.associatedData
|
||||
if ar.aeadCrypter.packetTag == packetTypeAEADEncrypted {
|
||||
adata = append(ar.associatedData, ar.chunkIndex...)
|
||||
}
|
||||
|
||||
nonce := ar.computeNextNonce()
|
||||
plainChunk, err := ar.aead.Open(data[:0:len(data)], nonce, data, adata)
|
||||
if err != nil {
|
||||
return nil, errors.ErrAEADTagVerification
|
||||
}
|
||||
ar.bytesProcessed += len(plainChunk)
|
||||
if err = ar.aeadCrypter.incrementIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainChunk, nil
|
||||
}
|
||||
|
||||
// Checks the summary tag. It takes into account the total decrypted bytes into
|
||||
// the associated data. It returns an error, or nil if the tag is valid.
|
||||
func (ar *aeadDecrypter) validateFinalTag(tag []byte) error {
|
||||
// Associated: tag, version, cipher, aead, chunk size, ...
|
||||
amountBytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(amountBytes, uint64(ar.bytesProcessed))
|
||||
|
||||
adata := ar.associatedData
|
||||
if ar.aeadCrypter.packetTag == packetTypeAEADEncrypted {
|
||||
// ... index ...
|
||||
adata = append(ar.associatedData, ar.chunkIndex...)
|
||||
}
|
||||
|
||||
// ... and total number of encrypted octets
|
||||
adata = append(adata, amountBytes...)
|
||||
nonce := ar.computeNextNonce()
|
||||
if _, err := ar.aead.Open(nil, nonce, tag, adata); err != nil {
|
||||
return errors.ErrAEADTagVerification
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// aeadEncrypter encrypts and writes bytes. It encrypts when necessary according
|
||||
// to the AEAD block size, and buffers the extra encrypted bytes for next write.
|
||||
type aeadEncrypter struct {
|
||||
aeadCrypter // Embedded plaintext sealer
|
||||
writer io.WriteCloser // 'writer' is a partialLengthWriter
|
||||
chunkBytes []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
// Write encrypts and writes bytes. It encrypts when necessary and buffers extra
|
||||
// plaintext bytes for next call. When the stream is finished, Close() MUST be
|
||||
// called to append the final tag.
|
||||
func (aw *aeadEncrypter) Write(plaintextBytes []byte) (n int, err error) {
|
||||
for n != len(plaintextBytes) {
|
||||
copied := copy(aw.chunkBytes[aw.offset:aw.chunkSize], plaintextBytes[n:])
|
||||
n += copied
|
||||
aw.offset += copied
|
||||
|
||||
if aw.offset == aw.chunkSize {
|
||||
encryptedChunk, err := aw.sealChunk(aw.chunkBytes[:aw.offset])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
_, err = aw.writer.Write(encryptedChunk)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
aw.offset = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close encrypts and writes the remaining buffered plaintext if any, appends
|
||||
// the final authentication tag, and closes the embedded writer. This function
|
||||
// MUST be called at the end of a stream.
|
||||
func (aw *aeadEncrypter) Close() (err error) {
|
||||
// Encrypt and write a chunk if there's buffered data left, or if we haven't
|
||||
// written any chunks yet.
|
||||
if aw.offset > 0 || aw.bytesProcessed == 0 {
|
||||
lastEncryptedChunk, err := aw.sealChunk(aw.chunkBytes[:aw.offset])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = aw.writer.Write(lastEncryptedChunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Compute final tag (associated data: packet tag, version, cipher, aead,
|
||||
// chunk size...
|
||||
adata := aw.associatedData
|
||||
|
||||
if aw.aeadCrypter.packetTag == packetTypeAEADEncrypted {
|
||||
// ... index ...
|
||||
adata = append(aw.associatedData, aw.chunkIndex...)
|
||||
}
|
||||
|
||||
// ... and total number of encrypted octets
|
||||
amountBytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(amountBytes, uint64(aw.bytesProcessed))
|
||||
adata = append(adata, amountBytes...)
|
||||
|
||||
nonce := aw.computeNextNonce()
|
||||
finalTag := aw.aead.Seal(nil, nonce, nil, adata)
|
||||
_, err = aw.writer.Write(finalTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return aw.writer.Close()
|
||||
}
|
||||
|
||||
// sealChunk Encrypts and authenticates the given chunk.
|
||||
func (aw *aeadEncrypter) sealChunk(data []byte) ([]byte, error) {
|
||||
if len(data) > aw.chunkSize {
|
||||
return nil, errors.AEADError("chunk exceeds maximum length")
|
||||
}
|
||||
if aw.associatedData == nil {
|
||||
return nil, errors.AEADError("can't seal without headers")
|
||||
}
|
||||
adata := aw.associatedData
|
||||
if aw.aeadCrypter.packetTag == packetTypeAEADEncrypted {
|
||||
adata = append(aw.associatedData, aw.chunkIndex...)
|
||||
}
|
||||
|
||||
nonce := aw.computeNextNonce()
|
||||
encrypted := aw.aead.Seal(data[:0], nonce, data, adata)
|
||||
aw.bytesProcessed += len(data)
|
||||
if err := aw.aeadCrypter.incrementIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encrypted, nil
|
||||
}
|
100
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go
generated
vendored
Normal file
100
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// AEADEncrypted represents an AEAD Encrypted Packet.
|
||||
// See https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-aead-encrypted-data-packet-t
|
||||
type AEADEncrypted struct {
|
||||
cipher CipherFunction
|
||||
mode AEADMode
|
||||
chunkSizeByte byte
|
||||
Contents io.Reader // Encrypted chunks and tags
|
||||
initialNonce []byte // Referred to as IV in RFC4880-bis
|
||||
}
|
||||
|
||||
// Only currently defined version
|
||||
const aeadEncryptedVersion = 1
|
||||
|
||||
func (ae *AEADEncrypted) parse(buf io.Reader) error {
|
||||
headerData := make([]byte, 4)
|
||||
if n, err := io.ReadFull(buf, headerData); n < 4 {
|
||||
return errors.AEADError("could not read aead header:" + err.Error())
|
||||
}
|
||||
// Read initial nonce
|
||||
mode := AEADMode(headerData[2])
|
||||
nonceLen := mode.IvLength()
|
||||
|
||||
// This packet supports only EAX and OCB
|
||||
// https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-aead-encrypted-data-packet-t
|
||||
if nonceLen == 0 || mode > AEADModeOCB {
|
||||
return errors.AEADError("unknown mode")
|
||||
}
|
||||
|
||||
initialNonce := make([]byte, nonceLen)
|
||||
if n, err := io.ReadFull(buf, initialNonce); n < nonceLen {
|
||||
return errors.AEADError("could not read aead nonce:" + err.Error())
|
||||
}
|
||||
ae.Contents = buf
|
||||
ae.initialNonce = initialNonce
|
||||
c := headerData[1]
|
||||
if _, ok := algorithm.CipherById[c]; !ok {
|
||||
return errors.UnsupportedError("unknown cipher: " + string(c))
|
||||
}
|
||||
ae.cipher = CipherFunction(c)
|
||||
ae.mode = mode
|
||||
ae.chunkSizeByte = headerData[3]
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or
|
||||
// an error.
|
||||
func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) {
|
||||
return ae.decrypt(key)
|
||||
}
|
||||
|
||||
// decrypt prepares an aeadCrypter and returns a ReadCloser from which
|
||||
// decrypted bytes can be read (see aeadDecrypter.Read()).
|
||||
func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) {
|
||||
blockCipher := ae.cipher.new(key)
|
||||
aead := ae.mode.new(blockCipher)
|
||||
// Carry the first tagLen bytes
|
||||
chunkSize := decodeAEADChunkSize(ae.chunkSizeByte)
|
||||
tagLen := ae.mode.TagLength()
|
||||
chunkBytes := make([]byte, chunkSize+tagLen*2)
|
||||
peekedBytes := chunkBytes[chunkSize+tagLen:]
|
||||
n, err := io.ReadFull(ae.Contents, peekedBytes)
|
||||
if n < tagLen || (err != nil && err != io.EOF) {
|
||||
return nil, errors.AEADError("Not enough data to decrypt:" + err.Error())
|
||||
}
|
||||
|
||||
return &aeadDecrypter{
|
||||
aeadCrypter: aeadCrypter{
|
||||
aead: aead,
|
||||
chunkSize: chunkSize,
|
||||
nonce: ae.initialNonce,
|
||||
associatedData: ae.associatedData(),
|
||||
chunkIndex: make([]byte, 8),
|
||||
packetTag: packetTypeAEADEncrypted,
|
||||
},
|
||||
reader: ae.Contents,
|
||||
chunkBytes: chunkBytes,
|
||||
peekedBytes: peekedBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// associatedData for chunks: tag, version, cipher, mode, chunk size byte
|
||||
func (ae *AEADEncrypted) associatedData() []byte {
|
||||
return []byte{
|
||||
0xD4,
|
||||
aeadEncryptedVersion,
|
||||
byte(ae.cipher),
|
||||
byte(ae.mode),
|
||||
ae.chunkSizeByte}
|
||||
}
|
161
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
generated
vendored
Normal file
161
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"compress/flate"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// Compressed represents a compressed OpenPGP packet. The decompressed contents
|
||||
// will contain more OpenPGP packets. See RFC 4880, section 5.6.
|
||||
type Compressed struct {
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
const (
|
||||
NoCompression = flate.NoCompression
|
||||
BestSpeed = flate.BestSpeed
|
||||
BestCompression = flate.BestCompression
|
||||
DefaultCompression = flate.DefaultCompression
|
||||
)
|
||||
|
||||
// CompressionConfig contains compressor configuration settings.
|
||||
type CompressionConfig struct {
|
||||
// Level is the compression level to use. It must be set to
|
||||
// between -1 and 9, with -1 causing the compressor to use the
|
||||
// default compression level, 0 causing the compressor to use
|
||||
// no compression and 1 to 9 representing increasing (better,
|
||||
// slower) compression levels. If Level is less than -1 or
|
||||
// more then 9, a non-nil error will be returned during
|
||||
// encryption. See the constants above for convenient common
|
||||
// settings for Level.
|
||||
Level int
|
||||
}
|
||||
|
||||
// decompressionReader ensures that the whole compression packet is read.
|
||||
type decompressionReader struct {
|
||||
compressed io.Reader
|
||||
decompressed io.ReadCloser
|
||||
readAll bool
|
||||
}
|
||||
|
||||
func newDecompressionReader(r io.Reader, decompressor io.ReadCloser) *decompressionReader {
|
||||
return &decompressionReader{
|
||||
compressed: r,
|
||||
decompressed: decompressor,
|
||||
}
|
||||
}
|
||||
|
||||
func (dr *decompressionReader) Read(data []byte) (n int, err error) {
|
||||
if dr.readAll {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err = dr.decompressed.Read(data)
|
||||
if err == io.EOF {
|
||||
dr.readAll = true
|
||||
// Close the decompressor.
|
||||
if errDec := dr.decompressed.Close(); errDec != nil {
|
||||
return n, errDec
|
||||
}
|
||||
// Consume all remaining data from the compressed packet.
|
||||
consumeAll(dr.compressed)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *Compressed) parse(r io.Reader) error {
|
||||
var buf [1]byte
|
||||
_, err := readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case 0:
|
||||
c.Body = r
|
||||
case 1:
|
||||
c.Body = newDecompressionReader(r, flate.NewReader(r))
|
||||
case 2:
|
||||
decompressor, err := zlib.NewReader(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Body = newDecompressionReader(r, decompressor)
|
||||
case 3:
|
||||
c.Body = newDecompressionReader(r, io.NopCloser(bzip2.NewReader(r)))
|
||||
default:
|
||||
err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// compressedWriterCloser represents the serialized compression stream
|
||||
// header and the compressor. Its Close() method ensures that both the
|
||||
// compressor and serialized stream header are closed. Its Write()
|
||||
// method writes to the compressor.
|
||||
type compressedWriteCloser struct {
|
||||
sh io.Closer // Stream Header
|
||||
c io.WriteCloser // Compressor
|
||||
}
|
||||
|
||||
func (cwc compressedWriteCloser) Write(p []byte) (int, error) {
|
||||
return cwc.c.Write(p)
|
||||
}
|
||||
|
||||
func (cwc compressedWriteCloser) Close() (err error) {
|
||||
err = cwc.c.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cwc.sh.Close()
|
||||
}
|
||||
|
||||
// SerializeCompressed serializes a compressed data packet to w and
|
||||
// returns a WriteCloser to which the literal data packets themselves
|
||||
// can be written and which MUST be closed on completion. If cc is
|
||||
// nil, sensible defaults will be used to configure the compression
|
||||
// algorithm.
|
||||
func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) {
|
||||
compressed, err := serializeStreamHeader(w, packetTypeCompressed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = compressed.Write([]byte{uint8(algo)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
level := DefaultCompression
|
||||
if cc != nil {
|
||||
level = cc.Level
|
||||
}
|
||||
|
||||
var compressor io.WriteCloser
|
||||
switch algo {
|
||||
case CompressionZIP:
|
||||
compressor, err = flate.NewWriter(compressed, level)
|
||||
case CompressionZLIB:
|
||||
compressor, err = zlib.NewWriterLevel(compressed, level)
|
||||
default:
|
||||
s := strconv.Itoa(int(algo))
|
||||
err = errors.UnsupportedError("Unsupported compression algorithm: " + s)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
literaldata = compressedWriteCloser{compressed, compressor}
|
||||
|
||||
return
|
||||
}
|
422
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
Normal file
422
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
// Copyright 2012 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
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultRejectPublicKeyAlgorithms = map[PublicKeyAlgorithm]bool{
|
||||
PubKeyAlgoElGamal: true,
|
||||
PubKeyAlgoDSA: true,
|
||||
}
|
||||
defaultRejectHashAlgorithms = map[crypto.Hash]bool{
|
||||
crypto.MD5: true,
|
||||
crypto.RIPEMD160: true,
|
||||
}
|
||||
defaultRejectMessageHashAlgorithms = map[crypto.Hash]bool{
|
||||
crypto.SHA1: true,
|
||||
crypto.MD5: true,
|
||||
crypto.RIPEMD160: true,
|
||||
}
|
||||
defaultRejectCurves = map[Curve]bool{
|
||||
CurveSecP256k1: true,
|
||||
}
|
||||
)
|
||||
|
||||
// A global feature flag to indicate v5 support.
|
||||
// Can be set via a build tag, e.g.: `go build -tags v5 ./...`
|
||||
// If the build tag is missing config_v5.go will set it to true.
|
||||
//
|
||||
// Disables parsing of v5 keys and v5 signatures.
|
||||
// These are non-standard entities, which in the crypto-refresh have been superseded
|
||||
// by v6 keys, v6 signatures and SEIPDv2 encrypted data, respectively.
|
||||
var V5Disabled = false
|
||||
|
||||
// Config collects a number of parameters along with sensible defaults.
|
||||
// A nil *Config is valid and results in all default values.
|
||||
type Config struct {
|
||||
// Rand provides the source of entropy.
|
||||
// If nil, the crypto/rand Reader is used.
|
||||
Rand io.Reader
|
||||
// DefaultHash is the default hash function to be used.
|
||||
// If zero, SHA-256 is used.
|
||||
DefaultHash crypto.Hash
|
||||
// DefaultCipher is the cipher to be used.
|
||||
// If zero, AES-128 is used.
|
||||
DefaultCipher CipherFunction
|
||||
// Time returns the current time as the number of seconds since the
|
||||
// epoch. If Time is nil, time.Now is used.
|
||||
Time func() time.Time
|
||||
// DefaultCompressionAlgo is the compression algorithm to be
|
||||
// applied to the plaintext before encryption. If zero, no
|
||||
// compression is done.
|
||||
DefaultCompressionAlgo CompressionAlgo
|
||||
// CompressionConfig configures the compression settings.
|
||||
CompressionConfig *CompressionConfig
|
||||
// S2K (String to Key) config, used for key derivation in the context of secret key encryption
|
||||
// and password-encrypted data.
|
||||
// If nil, the default configuration is used
|
||||
S2KConfig *s2k.Config
|
||||
// Iteration count for Iterated S2K (String to Key).
|
||||
// Only used if sk2.Mode is nil.
|
||||
// This value is duplicated here from s2k.Config for backwards compatibility.
|
||||
// It determines the strength of the passphrase stretching when
|
||||
// the said passphrase is hashed to produce a key. S2KCount
|
||||
// should be between 65536 and 65011712, inclusive. If Config
|
||||
// is nil or S2KCount is 0, the value 16777216 used. Not all
|
||||
// values in the above range can be represented. S2KCount will
|
||||
// be rounded up to the next representable value if it cannot
|
||||
// be encoded exactly. When set, it is strongly encrouraged to
|
||||
// use a value that is at least 65536. See RFC 4880 Section
|
||||
// 3.7.1.3.
|
||||
//
|
||||
// Deprecated: SK2Count should be configured in S2KConfig instead.
|
||||
S2KCount int
|
||||
// RSABits is the number of bits in new RSA keys made with NewEntity.
|
||||
// If zero, then 2048 bit keys are created.
|
||||
RSABits int
|
||||
// The public key algorithm to use - will always create a signing primary
|
||||
// key and encryption subkey.
|
||||
Algorithm PublicKeyAlgorithm
|
||||
// Some known primes that are optionally prepopulated by the caller
|
||||
RSAPrimes []*big.Int
|
||||
// Curve configures the desired packet.Curve if the Algorithm is PubKeyAlgoECDSA,
|
||||
// PubKeyAlgoEdDSA, or PubKeyAlgoECDH. If empty Curve25519 is used.
|
||||
Curve Curve
|
||||
// AEADConfig configures the use of the new AEAD Encrypted Data Packet,
|
||||
// defined in the draft of the next version of the OpenPGP specification.
|
||||
// If a non-nil AEADConfig is passed, usage of this packet is enabled. By
|
||||
// default, it is disabled. See the documentation of AEADConfig for more
|
||||
// configuration options related to AEAD.
|
||||
// **Note: using this option may break compatibility with other OpenPGP
|
||||
// implementations, as well as future versions of this library.**
|
||||
AEADConfig *AEADConfig
|
||||
// V6Keys configures version 6 key generation. If false, this package still
|
||||
// supports version 6 keys, but produces version 4 keys.
|
||||
V6Keys bool
|
||||
// Minimum RSA key size allowed for key generation and message signing, verification and encryption.
|
||||
MinRSABits uint16
|
||||
// Reject insecure algorithms, only works with v2 api
|
||||
RejectPublicKeyAlgorithms map[PublicKeyAlgorithm]bool
|
||||
RejectHashAlgorithms map[crypto.Hash]bool
|
||||
RejectMessageHashAlgorithms map[crypto.Hash]bool
|
||||
RejectCurves map[Curve]bool
|
||||
// "The validity period of the key. This is the number of seconds after
|
||||
// the key creation time that the key expires. If this is not present
|
||||
// or has a value of zero, the key never expires. This is found only on
|
||||
// a self-signature.""
|
||||
// https://tools.ietf.org/html/rfc4880#section-5.2.3.6
|
||||
KeyLifetimeSecs uint32
|
||||
// "The validity period of the signature. This is the number of seconds
|
||||
// after the signature creation time that the signature expires. If
|
||||
// this is not present or has a value of zero, it never expires."
|
||||
// https://tools.ietf.org/html/rfc4880#section-5.2.3.10
|
||||
SigLifetimeSecs uint32
|
||||
// SigningKeyId is used to specify the signing key to use (by Key ID).
|
||||
// By default, the signing key is selected automatically, preferring
|
||||
// signing subkeys if available.
|
||||
SigningKeyId uint64
|
||||
// SigningIdentity is used to specify a user ID (packet Signer's User ID, type 28)
|
||||
// when producing a generic certification signature onto an existing user ID.
|
||||
// The identity must be present in the signer Entity.
|
||||
SigningIdentity string
|
||||
// InsecureAllowUnauthenticatedMessages controls, whether it is tolerated to read
|
||||
// encrypted messages without Modification Detection Code (MDC).
|
||||
// MDC is mandated by the IETF OpenPGP Crypto Refresh draft and has long been implemented
|
||||
// in most OpenPGP implementations. Messages without MDC are considered unnecessarily
|
||||
// insecure and should be prevented whenever possible.
|
||||
// In case one needs to deal with messages from very old OpenPGP implementations, there
|
||||
// might be no other way than to tolerate the missing MDC. Setting this flag, allows this
|
||||
// mode of operation. It should be considered a measure of last resort.
|
||||
InsecureAllowUnauthenticatedMessages bool
|
||||
// InsecureAllowDecryptionWithSigningKeys allows decryption with keys marked as signing keys in the v2 API.
|
||||
// This setting is potentially insecure, but it is needed as some libraries
|
||||
// ignored key flags when selecting a key for encryption.
|
||||
// Not relevant for the v1 API, as all keys were allowed in decryption.
|
||||
InsecureAllowDecryptionWithSigningKeys bool
|
||||
// KnownNotations is a map of Notation Data names to bools, which controls
|
||||
// the notation names that are allowed to be present in critical Notation Data
|
||||
// signature subpackets.
|
||||
KnownNotations map[string]bool
|
||||
// SignatureNotations is a list of Notations to be added to any signatures.
|
||||
SignatureNotations []*Notation
|
||||
// CheckIntendedRecipients controls, whether the OpenPGP Intended Recipient Fingerprint feature
|
||||
// should be enabled for encryption and decryption.
|
||||
// (See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-intended-recipient-fingerpr).
|
||||
// When the flag is set, encryption produces Intended Recipient Fingerprint signature sub-packets and decryption
|
||||
// checks whether the key it was encrypted to is one of the included fingerprints in the signature.
|
||||
// If the flag is disabled, no Intended Recipient Fingerprint sub-packets are created or checked.
|
||||
// The default behavior, when the config or flag is nil, is to enable the feature.
|
||||
CheckIntendedRecipients *bool
|
||||
// CacheSessionKey controls if decryption should return the session key used for decryption.
|
||||
// If the flag is set, the session key is cached in the message details struct.
|
||||
CacheSessionKey bool
|
||||
// CheckPacketSequence is a flag that controls if the pgp message reader should strictly check
|
||||
// that the packet sequence conforms with the grammar mandated by rfc4880.
|
||||
// The default behavior, when the config or flag is nil, is to check the packet sequence.
|
||||
CheckPacketSequence *bool
|
||||
// NonDeterministicSignaturesViaNotation is a flag to enable randomization of signatures.
|
||||
// If true, a salt notation is used to randomize signatures generated by v4 and v5 keys
|
||||
// (v6 signatures are always non-deterministic, by design).
|
||||
// This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur
|
||||
// during the signing computation. It is added to signatures of any algo for simplicity, and as it may also serve as protection in case of
|
||||
// weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
|
||||
// The default behavior, when the config or flag is nil, is to enable the feature.
|
||||
NonDeterministicSignaturesViaNotation *bool
|
||||
|
||||
// InsecureAllowAllKeyFlagsWhenMissing determines how a key without valid key flags is handled.
|
||||
// When set to true, a key without flags is treated as if all flags are enabled.
|
||||
// This behavior is consistent with GPG.
|
||||
InsecureAllowAllKeyFlagsWhenMissing bool
|
||||
}
|
||||
|
||||
func (c *Config) Random() io.Reader {
|
||||
if c == nil || c.Rand == nil {
|
||||
return rand.Reader
|
||||
}
|
||||
return c.Rand
|
||||
}
|
||||
|
||||
func (c *Config) Hash() crypto.Hash {
|
||||
if c == nil || uint(c.DefaultHash) == 0 {
|
||||
return crypto.SHA256
|
||||
}
|
||||
return c.DefaultHash
|
||||
}
|
||||
|
||||
func (c *Config) Cipher() CipherFunction {
|
||||
if c == nil || uint8(c.DefaultCipher) == 0 {
|
||||
return CipherAES128
|
||||
}
|
||||
return c.DefaultCipher
|
||||
}
|
||||
|
||||
func (c *Config) Now() time.Time {
|
||||
if c == nil || c.Time == nil {
|
||||
return time.Now().Truncate(time.Second)
|
||||
}
|
||||
return c.Time().Truncate(time.Second)
|
||||
}
|
||||
|
||||
// KeyLifetime returns the validity period of the key.
|
||||
func (c *Config) KeyLifetime() uint32 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.KeyLifetimeSecs
|
||||
}
|
||||
|
||||
// SigLifetime returns the validity period of the signature.
|
||||
func (c *Config) SigLifetime() uint32 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.SigLifetimeSecs
|
||||
}
|
||||
|
||||
func (c *Config) Compression() CompressionAlgo {
|
||||
if c == nil {
|
||||
return CompressionNone
|
||||
}
|
||||
return c.DefaultCompressionAlgo
|
||||
}
|
||||
|
||||
func (c *Config) RSAModulusBits() int {
|
||||
if c == nil || c.RSABits == 0 {
|
||||
return 2048
|
||||
}
|
||||
return c.RSABits
|
||||
}
|
||||
|
||||
func (c *Config) PublicKeyAlgorithm() PublicKeyAlgorithm {
|
||||
if c == nil || c.Algorithm == 0 {
|
||||
return PubKeyAlgoRSA
|
||||
}
|
||||
return c.Algorithm
|
||||
}
|
||||
|
||||
func (c *Config) CurveName() Curve {
|
||||
if c == nil || c.Curve == "" {
|
||||
return Curve25519
|
||||
}
|
||||
return c.Curve
|
||||
}
|
||||
|
||||
// Deprecated: The hash iterations should now be queried via the S2K() method.
|
||||
func (c *Config) PasswordHashIterations() int {
|
||||
if c == nil || c.S2KCount == 0 {
|
||||
return 0
|
||||
}
|
||||
return c.S2KCount
|
||||
}
|
||||
|
||||
func (c *Config) S2K() *s2k.Config {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
// for backwards compatibility
|
||||
if c.S2KCount > 0 && c.S2KConfig == nil {
|
||||
return &s2k.Config{
|
||||
S2KCount: c.S2KCount,
|
||||
}
|
||||
}
|
||||
return c.S2KConfig
|
||||
}
|
||||
|
||||
func (c *Config) AEAD() *AEADConfig {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.AEADConfig
|
||||
}
|
||||
|
||||
func (c *Config) SigningKey() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.SigningKeyId
|
||||
}
|
||||
|
||||
func (c *Config) SigningUserId() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.SigningIdentity
|
||||
}
|
||||
|
||||
func (c *Config) AllowUnauthenticatedMessages() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureAllowUnauthenticatedMessages
|
||||
}
|
||||
|
||||
func (c *Config) AllowDecryptionWithSigningKeys() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureAllowDecryptionWithSigningKeys
|
||||
}
|
||||
|
||||
func (c *Config) KnownNotation(notationName string) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.KnownNotations[notationName]
|
||||
}
|
||||
|
||||
func (c *Config) Notations() []*Notation {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.SignatureNotations
|
||||
}
|
||||
|
||||
func (c *Config) V6() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.V6Keys
|
||||
}
|
||||
|
||||
func (c *Config) IntendedRecipients() bool {
|
||||
if c == nil || c.CheckIntendedRecipients == nil {
|
||||
return true
|
||||
}
|
||||
return *c.CheckIntendedRecipients
|
||||
}
|
||||
|
||||
func (c *Config) RetrieveSessionKey() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.CacheSessionKey
|
||||
}
|
||||
|
||||
func (c *Config) MinimumRSABits() uint16 {
|
||||
if c == nil || c.MinRSABits == 0 {
|
||||
return 2047
|
||||
}
|
||||
return c.MinRSABits
|
||||
}
|
||||
|
||||
func (c *Config) RejectPublicKeyAlgorithm(alg PublicKeyAlgorithm) bool {
|
||||
var rejectedAlgorithms map[PublicKeyAlgorithm]bool
|
||||
if c == nil || c.RejectPublicKeyAlgorithms == nil {
|
||||
// Default
|
||||
rejectedAlgorithms = defaultRejectPublicKeyAlgorithms
|
||||
} else {
|
||||
rejectedAlgorithms = c.RejectPublicKeyAlgorithms
|
||||
}
|
||||
return rejectedAlgorithms[alg]
|
||||
}
|
||||
|
||||
func (c *Config) RejectHashAlgorithm(hash crypto.Hash) bool {
|
||||
var rejectedAlgorithms map[crypto.Hash]bool
|
||||
if c == nil || c.RejectHashAlgorithms == nil {
|
||||
// Default
|
||||
rejectedAlgorithms = defaultRejectHashAlgorithms
|
||||
} else {
|
||||
rejectedAlgorithms = c.RejectHashAlgorithms
|
||||
}
|
||||
return rejectedAlgorithms[hash]
|
||||
}
|
||||
|
||||
func (c *Config) RejectMessageHashAlgorithm(hash crypto.Hash) bool {
|
||||
var rejectedAlgorithms map[crypto.Hash]bool
|
||||
if c == nil || c.RejectMessageHashAlgorithms == nil {
|
||||
// Default
|
||||
rejectedAlgorithms = defaultRejectMessageHashAlgorithms
|
||||
} else {
|
||||
rejectedAlgorithms = c.RejectMessageHashAlgorithms
|
||||
}
|
||||
return rejectedAlgorithms[hash]
|
||||
}
|
||||
|
||||
func (c *Config) RejectCurve(curve Curve) bool {
|
||||
var rejectedCurve map[Curve]bool
|
||||
if c == nil || c.RejectCurves == nil {
|
||||
// Default
|
||||
rejectedCurve = defaultRejectCurves
|
||||
} else {
|
||||
rejectedCurve = c.RejectCurves
|
||||
}
|
||||
return rejectedCurve[curve]
|
||||
}
|
||||
|
||||
func (c *Config) StrictPacketSequence() bool {
|
||||
if c == nil || c.CheckPacketSequence == nil {
|
||||
return true
|
||||
}
|
||||
return *c.CheckPacketSequence
|
||||
}
|
||||
|
||||
func (c *Config) RandomizeSignaturesViaNotation() bool {
|
||||
if c == nil || c.NonDeterministicSignaturesViaNotation == nil {
|
||||
return true
|
||||
}
|
||||
return *c.NonDeterministicSignaturesViaNotation
|
||||
}
|
||||
|
||||
func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureAllowAllKeyFlagsWhenMissing
|
||||
}
|
||||
|
||||
// BoolPointer is a helper function to set a boolean pointer in the Config.
|
||||
// e.g., config.CheckPacketSequence = BoolPointer(true)
|
||||
func BoolPointer(value bool) *bool {
|
||||
return &value
|
||||
}
|
7
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go
generated
vendored
Normal file
7
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
//go:build !v5
|
||||
|
||||
package packet
|
||||
|
||||
func init() {
|
||||
V5Disabled = true
|
||||
}
|
584
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
generated
vendored
Normal file
584
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
generated
vendored
Normal file
@ -0,0 +1,584 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x448"
|
||||
)
|
||||
|
||||
// EncryptedKey represents a public-key encrypted session key. See RFC 4880,
|
||||
// section 5.1.
|
||||
type EncryptedKey struct {
|
||||
Version int
|
||||
KeyId uint64
|
||||
KeyVersion int // v6
|
||||
KeyFingerprint []byte // v6
|
||||
Algo PublicKeyAlgorithm
|
||||
CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet
|
||||
Key []byte // only valid after a successful Decrypt
|
||||
|
||||
encryptedMPI1, encryptedMPI2 encoding.Field
|
||||
ephemeralPublicX25519 *x25519.PublicKey // used for x25519
|
||||
ephemeralPublicX448 *x448.PublicKey // used for x448
|
||||
encryptedSession []byte // used for x25519 and x448
|
||||
}
|
||||
|
||||
func (e *EncryptedKey) parse(r io.Reader) (err error) {
|
||||
var buf [8]byte
|
||||
_, err = readFull(r, buf[:versionSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.Version = int(buf[0])
|
||||
if e.Version != 3 && e.Version != 6 {
|
||||
return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if e.Version == 6 {
|
||||
//Read a one-octet size of the following two fields.
|
||||
if _, err = readFull(r, buf[:1]); err != nil {
|
||||
return
|
||||
}
|
||||
// The size may also be zero, and the key version and
|
||||
// fingerprint omitted for an "anonymous recipient"
|
||||
if buf[0] != 0 {
|
||||
// non-anonymous case
|
||||
_, err = readFull(r, buf[:versionSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.KeyVersion = int(buf[0])
|
||||
if e.KeyVersion != 4 && e.KeyVersion != 6 {
|
||||
return errors.UnsupportedError("unknown public key version " + strconv.Itoa(e.KeyVersion))
|
||||
}
|
||||
var fingerprint []byte
|
||||
if e.KeyVersion == 6 {
|
||||
fingerprint = make([]byte, fingerprintSizeV6)
|
||||
} else if e.KeyVersion == 4 {
|
||||
fingerprint = make([]byte, fingerprintSize)
|
||||
}
|
||||
_, err = readFull(r, fingerprint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.KeyFingerprint = fingerprint
|
||||
if e.KeyVersion == 6 {
|
||||
e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[:keyIdSize])
|
||||
} else if e.KeyVersion == 4 {
|
||||
e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[fingerprintSize-keyIdSize : fingerprintSize])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err = readFull(r, buf[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.KeyId = binary.BigEndian.Uint64(buf[:keyIdSize])
|
||||
}
|
||||
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.Algo = PublicKeyAlgorithm(buf[0])
|
||||
var cipherFunction byte
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoElGamal:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e.encryptedMPI2 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoECDH:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e.encryptedMPI2 = new(encoding.OID)
|
||||
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoX25519:
|
||||
e.ephemeralPublicX25519, e.encryptedSession, cipherFunction, err = x25519.DecodeFields(r, e.Version == 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoX448:
|
||||
e.ephemeralPublicX448, e.encryptedSession, cipherFunction, err = x448.DecodeFields(r, e.Version == 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if e.Version < 6 {
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
e.CipherFunc = CipherFunction(cipherFunction)
|
||||
// Check for validiy is in the Decrypt method
|
||||
}
|
||||
}
|
||||
|
||||
_, err = consumeAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted session key with the given private key. The
|
||||
// private key must have been decrypted first.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
|
||||
if e.Version < 6 && e.KeyId != 0 && e.KeyId != priv.KeyId {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key for key id " + strconv.FormatUint(e.KeyId, 16) + " with private key id " + strconv.FormatUint(priv.KeyId, 16))
|
||||
}
|
||||
if e.Version == 6 && e.KeyVersion != 0 && !bytes.Equal(e.KeyFingerprint, priv.Fingerprint) {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key for key fingerprint " + hex.EncodeToString(e.KeyFingerprint) + " with private key fingerprint " + hex.EncodeToString(priv.Fingerprint))
|
||||
}
|
||||
if e.Algo != priv.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key of type " + strconv.Itoa(int(e.Algo)) + " with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
if priv.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
|
||||
var err error
|
||||
var b []byte
|
||||
|
||||
// TODO(agl): use session key decryption routines here to avoid
|
||||
// padding oracle attacks.
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
// Supports both *rsa.PrivateKey and crypto.Decrypter
|
||||
k := priv.PrivateKey.(crypto.Decrypter)
|
||||
b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.Bytes()), nil)
|
||||
case PubKeyAlgoElGamal:
|
||||
c1 := new(big.Int).SetBytes(e.encryptedMPI1.Bytes())
|
||||
c2 := new(big.Int).SetBytes(e.encryptedMPI2.Bytes())
|
||||
b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2)
|
||||
case PubKeyAlgoECDH:
|
||||
vsG := e.encryptedMPI1.Bytes()
|
||||
m := e.encryptedMPI2.Bytes()
|
||||
oid := priv.PublicKey.oid.EncodedBytes()
|
||||
fp := priv.PublicKey.Fingerprint[:]
|
||||
if priv.PublicKey.Version == 5 {
|
||||
// For v5 the, the fingerprint must be restricted to 20 bytes
|
||||
fp = fp[:20]
|
||||
}
|
||||
b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, fp)
|
||||
case PubKeyAlgoX25519:
|
||||
b, err = x25519.Decrypt(priv.PrivateKey.(*x25519.PrivateKey), e.ephemeralPublicX25519, e.encryptedSession)
|
||||
case PubKeyAlgoX448:
|
||||
b, err = x448.Decrypt(priv.PrivateKey.(*x448.PrivateKey), e.ephemeralPublicX448, e.encryptedSession)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var key []byte
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
|
||||
keyOffset := 0
|
||||
if e.Version < 6 {
|
||||
e.CipherFunc = CipherFunction(b[0])
|
||||
keyOffset = 1
|
||||
if !e.CipherFunc.IsSupported() {
|
||||
return errors.UnsupportedError("unsupported encryption function")
|
||||
}
|
||||
}
|
||||
key, err = decodeChecksumKey(b[keyOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
if e.Version < 6 {
|
||||
switch e.CipherFunc {
|
||||
case CipherAES128, CipherAES192, CipherAES256:
|
||||
break
|
||||
default:
|
||||
return errors.StructuralError("v3 PKESK mandates AES as cipher function for x25519 and x448")
|
||||
}
|
||||
}
|
||||
key = b[:]
|
||||
default:
|
||||
return errors.UnsupportedError("unsupported algorithm for decryption")
|
||||
}
|
||||
e.Key = key
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the encrypted key packet, e, to w.
|
||||
func (e *EncryptedKey) Serialize(w io.Writer) error {
|
||||
var encodedLength int
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
encodedLength = int(e.encryptedMPI1.EncodedLength())
|
||||
case PubKeyAlgoElGamal:
|
||||
encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
case PubKeyAlgoECDH:
|
||||
encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
case PubKeyAlgoX25519:
|
||||
encodedLength = x25519.EncodedFieldsLength(e.encryptedSession, e.Version == 6)
|
||||
case PubKeyAlgoX448:
|
||||
encodedLength = x448.EncodedFieldsLength(e.encryptedSession, e.Version == 6)
|
||||
default:
|
||||
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
|
||||
}
|
||||
|
||||
packetLen := versionSize /* version */ + keyIdSize /* key id */ + algorithmSize /* algo */ + encodedLength
|
||||
if e.Version == 6 {
|
||||
packetLen = versionSize /* version */ + algorithmSize /* algo */ + encodedLength + keyVersionSize /* key version */
|
||||
if e.KeyVersion == 6 {
|
||||
packetLen += fingerprintSizeV6
|
||||
} else if e.KeyVersion == 4 {
|
||||
packetLen += fingerprintSize
|
||||
}
|
||||
}
|
||||
|
||||
err := serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte{byte(e.Version)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.Version == 6 {
|
||||
_, err = w.Write([]byte{byte(e.KeyVersion)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The key version number may also be zero,
|
||||
// and the fingerprint omitted
|
||||
if e.KeyVersion != 0 {
|
||||
_, err = w.Write(e.KeyFingerprint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Write KeyID
|
||||
err = binary.Write(w, binary.BigEndian, e.KeyId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = w.Write([]byte{byte(e.Algo)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
_, err := w.Write(e.encryptedMPI1.EncodedBytes())
|
||||
return err
|
||||
case PubKeyAlgoElGamal:
|
||||
if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
|
||||
return err
|
||||
case PubKeyAlgoECDH:
|
||||
if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
|
||||
return err
|
||||
case PubKeyAlgoX25519:
|
||||
err := x25519.EncodeFields(w, e.ephemeralPublicX25519, e.encryptedSession, byte(e.CipherFunc), e.Version == 6)
|
||||
return err
|
||||
case PubKeyAlgoX448:
|
||||
err := x448.EncodeFields(w, e.ephemeralPublicX448, e.encryptedSession, byte(e.CipherFunc), e.Version == 6)
|
||||
return err
|
||||
default:
|
||||
panic("internal error")
|
||||
}
|
||||
}
|
||||
|
||||
// SerializeEncryptedKeyAEAD serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// If aeadSupported is set, PKESK v6 is used, otherwise v3.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKeyAEAD(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, config *Config) error {
|
||||
return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, aeadSupported, key, false, config)
|
||||
}
|
||||
|
||||
// SerializeEncryptedKeyAEADwithHiddenOption serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// Offers the hidden flag option to indicated if the PKESK packet should include a wildcard KeyID.
|
||||
// If aeadSupported is set, PKESK v6 is used, otherwise v3.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKeyAEADwithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, hidden bool, config *Config) error {
|
||||
var buf [36]byte // max possible header size is v6
|
||||
lenHeaderWritten := versionSize
|
||||
version := 3
|
||||
|
||||
if aeadSupported {
|
||||
version = 6
|
||||
}
|
||||
// An implementation MUST NOT generate ElGamal v6 PKESKs.
|
||||
if version == 6 && pub.PubKeyAlgo == PubKeyAlgoElGamal {
|
||||
return errors.InvalidArgumentError("ElGamal v6 PKESK are not allowed")
|
||||
}
|
||||
// In v3 PKESKs, for x25519 and x448, mandate using AES
|
||||
if version == 3 && (pub.PubKeyAlgo == PubKeyAlgoX25519 || pub.PubKeyAlgo == PubKeyAlgoX448) {
|
||||
switch cipherFunc {
|
||||
case CipherAES128, CipherAES192, CipherAES256:
|
||||
break
|
||||
default:
|
||||
return errors.InvalidArgumentError("v3 PKESK mandates AES for x25519 and x448")
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = byte(version)
|
||||
|
||||
// If hidden is set, the key should be hidden
|
||||
// An implementation MAY accept or use a Key ID of all zeros,
|
||||
// or a key version of zero and no key fingerprint, to hide the intended decryption key.
|
||||
// See Section 5.1.8. in the open pgp crypto refresh
|
||||
if version == 6 {
|
||||
if !hidden {
|
||||
// A one-octet size of the following two fields.
|
||||
buf[1] = byte(keyVersionSize + len(pub.Fingerprint))
|
||||
// A one octet key version number.
|
||||
buf[2] = byte(pub.Version)
|
||||
lenHeaderWritten += keyVersionSize + 1
|
||||
// The fingerprint of the public key
|
||||
copy(buf[lenHeaderWritten:lenHeaderWritten+len(pub.Fingerprint)], pub.Fingerprint)
|
||||
lenHeaderWritten += len(pub.Fingerprint)
|
||||
} else {
|
||||
// The size may also be zero, and the key version
|
||||
// and fingerprint omitted for an "anonymous recipient"
|
||||
buf[1] = 0
|
||||
lenHeaderWritten += 1
|
||||
}
|
||||
} else {
|
||||
if !hidden {
|
||||
binary.BigEndian.PutUint64(buf[versionSize:(versionSize+keyIdSize)], pub.KeyId)
|
||||
}
|
||||
lenHeaderWritten += keyIdSize
|
||||
}
|
||||
buf[lenHeaderWritten] = byte(pub.PubKeyAlgo)
|
||||
lenHeaderWritten += algorithmSize
|
||||
|
||||
var keyBlock []byte
|
||||
switch pub.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
|
||||
lenKeyBlock := len(key) + 2
|
||||
if version < 6 {
|
||||
lenKeyBlock += 1 // cipher type included
|
||||
}
|
||||
keyBlock = make([]byte, lenKeyBlock)
|
||||
keyOffset := 0
|
||||
if version < 6 {
|
||||
keyBlock[0] = byte(cipherFunc)
|
||||
keyOffset = 1
|
||||
}
|
||||
encodeChecksumKey(keyBlock[keyOffset:], key)
|
||||
case PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
// algorithm is added in plaintext below
|
||||
keyBlock = key
|
||||
}
|
||||
|
||||
switch pub.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
return serializeEncryptedKeyRSA(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*rsa.PublicKey), keyBlock)
|
||||
case PubKeyAlgoElGamal:
|
||||
return serializeEncryptedKeyElGamal(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*elgamal.PublicKey), keyBlock)
|
||||
case PubKeyAlgoECDH:
|
||||
return serializeEncryptedKeyECDH(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
|
||||
case PubKeyAlgoX25519:
|
||||
return serializeEncryptedKeyX25519(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x25519.PublicKey), keyBlock, byte(cipherFunc), version)
|
||||
case PubKeyAlgoX448:
|
||||
return serializeEncryptedKeyX448(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x448.PublicKey), keyBlock, byte(cipherFunc), version)
|
||||
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
|
||||
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
// SerializeEncryptedKey serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// PKESKv6 is used if config.AEAD() is not nil.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
// Deprecated: Use SerializeEncryptedKeyAEAD instead.
|
||||
func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
|
||||
return SerializeEncryptedKeyAEAD(w, pub, cipherFunc, config.AEAD() != nil, key, config)
|
||||
}
|
||||
|
||||
// SerializeEncryptedKeyWithHiddenOption serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub. PKESKv6 is used if config.AEAD() is not nil.
|
||||
// The hidden option controls if the packet should be anonymous, i.e., omit key metadata.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
// Deprecated: Use SerializeEncryptedKeyAEADwithHiddenOption instead.
|
||||
func SerializeEncryptedKeyWithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, hidden bool, config *Config) error {
|
||||
return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, config.AEAD() != nil, key, hidden, config)
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header []byte, pub *rsa.PublicKey, keyBlock []byte) error {
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("RSA encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
cipherMPI := encoding.NewMPI(cipherText)
|
||||
packetLen := len(header) /* header length */ + int(cipherMPI.EncodedLength())
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(cipherMPI.EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header []byte, pub *elgamal.PublicKey, keyBlock []byte) error {
|
||||
c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8
|
||||
packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(new(encoding.MPI).SetBig(c1).EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(new(encoding.MPI).SetBig(c2).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header []byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error {
|
||||
vsG, c, err := ecdh.Encrypt(rand, pub, keyBlock, oid.EncodedBytes(), fingerprint)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ECDH encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
g := encoding.NewMPI(vsG)
|
||||
m := encoding.NewOID(c)
|
||||
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += int(g.EncodedLength()) + int(m.EncodedLength())
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(g.EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(m.EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyX25519(w io.Writer, rand io.Reader, header []byte, pub *x25519.PublicKey, keyBlock []byte, cipherFunc byte, version int) error {
|
||||
ephemeralPublicX25519, ciphertext, err := x25519.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("x25519 encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += x25519.EncodedFieldsLength(ciphertext, version == 6)
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return x25519.EncodeFields(w, ephemeralPublicX25519, ciphertext, cipherFunc, version == 6)
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyX448(w io.Writer, rand io.Reader, header []byte, pub *x448.PublicKey, keyBlock []byte, cipherFunc byte, version int) error {
|
||||
ephemeralPublicX448, ciphertext, err := x448.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("x448 encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += x448.EncodedFieldsLength(ciphertext, version == 6)
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return x448.EncodeFields(w, ephemeralPublicX448, ciphertext, cipherFunc, version == 6)
|
||||
}
|
||||
|
||||
func checksumKeyMaterial(key []byte) uint16 {
|
||||
var checksum uint16
|
||||
for _, v := range key {
|
||||
checksum += uint16(v)
|
||||
}
|
||||
return checksum
|
||||
}
|
||||
|
||||
func decodeChecksumKey(msg []byte) (key []byte, err error) {
|
||||
key = msg[:len(msg)-2]
|
||||
expectedChecksum := uint16(msg[len(msg)-2])<<8 | uint16(msg[len(msg)-1])
|
||||
checksum := checksumKeyMaterial(key)
|
||||
if checksum != expectedChecksum {
|
||||
err = errors.StructuralError("session key checksum is incorrect")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func encodeChecksumKey(buffer []byte, key []byte) {
|
||||
copy(buffer, key)
|
||||
checksum := checksumKeyMaterial(key)
|
||||
buffer[len(key)] = byte(checksum >> 8)
|
||||
buffer[len(key)+1] = byte(checksum)
|
||||
}
|
91
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
generated
vendored
Normal file
91
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// LiteralData represents an encrypted file. See RFC 4880, section 5.9.
|
||||
type LiteralData struct {
|
||||
Format uint8
|
||||
IsBinary bool
|
||||
FileName string
|
||||
Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined.
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
// ForEyesOnly returns whether the contents of the LiteralData have been marked
|
||||
// as especially sensitive.
|
||||
func (l *LiteralData) ForEyesOnly() bool {
|
||||
return l.FileName == "_CONSOLE"
|
||||
}
|
||||
|
||||
func (l *LiteralData) parse(r io.Reader) (err error) {
|
||||
var buf [256]byte
|
||||
|
||||
_, err = readFull(r, buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.Format = buf[0]
|
||||
l.IsBinary = l.Format == 'b'
|
||||
fileNameLen := int(buf[1])
|
||||
|
||||
_, err = readFull(r, buf[:fileNameLen])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.FileName = string(buf[:fileNameLen])
|
||||
|
||||
_, err = readFull(r, buf[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.Time = binary.BigEndian.Uint32(buf[:4])
|
||||
l.Body = r
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeLiteral serializes a literal data packet to w and returns a
|
||||
// WriteCloser to which the data itself can be written and which MUST be closed
|
||||
// on completion. The fileName is truncated to 255 bytes.
|
||||
func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) {
|
||||
var buf [4]byte
|
||||
buf[0] = 'b'
|
||||
if !isBinary {
|
||||
buf[0] = 'u'
|
||||
}
|
||||
if len(fileName) > 255 {
|
||||
fileName = fileName[:255]
|
||||
}
|
||||
buf[1] = byte(len(fileName))
|
||||
|
||||
inner, err := serializeStreamHeader(w, packetTypeLiteralData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = inner.Write(buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = inner.Write([]byte(fileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[:], time)
|
||||
_, err = inner.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
plaintext = inner
|
||||
return
|
||||
}
|
33
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go
generated
vendored
Normal file
33
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
type Marker struct{}
|
||||
|
||||
const markerString = "PGP"
|
||||
|
||||
// parse just checks if the packet contains "PGP".
|
||||
func (m *Marker) parse(reader io.Reader) error {
|
||||
var buffer [3]byte
|
||||
if _, err := io.ReadFull(reader, buffer[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if string(buffer[:]) != markerString {
|
||||
return errors.StructuralError("invalid marker packet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeMarker writes a marker packet to writer.
|
||||
func SerializeMarker(writer io.Writer) error {
|
||||
err := serializeHeader(writer, packetTypeMarker, len(markerString))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write([]byte(markerString))
|
||||
return err
|
||||
}
|
29
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/notation.go
generated
vendored
Normal file
29
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/notation.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package packet
|
||||
|
||||
// Notation type represents a Notation Data subpacket
|
||||
// see https://tools.ietf.org/html/rfc4880#section-5.2.3.16
|
||||
type Notation struct {
|
||||
Name string
|
||||
Value []byte
|
||||
IsCritical bool
|
||||
IsHumanReadable bool
|
||||
}
|
||||
|
||||
func (notation *Notation) getData() []byte {
|
||||
nameData := []byte(notation.Name)
|
||||
nameLen := len(nameData)
|
||||
valueLen := len(notation.Value)
|
||||
|
||||
data := make([]byte, 8+nameLen+valueLen)
|
||||
if notation.IsHumanReadable {
|
||||
data[0] = 0x80
|
||||
}
|
||||
|
||||
data[4] = byte(nameLen >> 8)
|
||||
data[5] = byte(nameLen)
|
||||
data[6] = byte(valueLen >> 8)
|
||||
data[7] = byte(valueLen)
|
||||
copy(data[8:8+nameLen], nameData)
|
||||
copy(data[8+nameLen:], notation.Value)
|
||||
return data
|
||||
}
|
137
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go
generated
vendored
Normal file
137
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
|
||||
// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type ocfbEncrypter struct {
|
||||
b cipher.Block
|
||||
fre []byte
|
||||
outUsed int
|
||||
}
|
||||
|
||||
// An OCFBResyncOption determines if the "resynchronization step" of OCFB is
|
||||
// performed.
|
||||
type OCFBResyncOption bool
|
||||
|
||||
const (
|
||||
OCFBResync OCFBResyncOption = true
|
||||
OCFBNoResync OCFBResyncOption = false
|
||||
)
|
||||
|
||||
// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's
|
||||
// cipher feedback mode using the given cipher.Block, and an initial amount of
|
||||
// ciphertext. randData must be random bytes and be the same length as the
|
||||
// cipher.Block's block size. Resync determines if the "resynchronization step"
|
||||
// from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on
|
||||
// this point.
|
||||
func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) {
|
||||
blockSize := block.BlockSize()
|
||||
if len(randData) != blockSize {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
x := &ocfbEncrypter{
|
||||
b: block,
|
||||
fre: make([]byte, blockSize),
|
||||
outUsed: 0,
|
||||
}
|
||||
prefix := make([]byte, blockSize+2)
|
||||
|
||||
block.Encrypt(x.fre, x.fre)
|
||||
for i := 0; i < blockSize; i++ {
|
||||
prefix[i] = randData[i] ^ x.fre[i]
|
||||
}
|
||||
|
||||
block.Encrypt(x.fre, prefix[:blockSize])
|
||||
prefix[blockSize] = x.fre[0] ^ randData[blockSize-2]
|
||||
prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1]
|
||||
|
||||
if resync {
|
||||
block.Encrypt(x.fre, prefix[2:])
|
||||
} else {
|
||||
x.fre[0] = prefix[blockSize]
|
||||
x.fre[1] = prefix[blockSize+1]
|
||||
x.outUsed = 2
|
||||
}
|
||||
return x, prefix
|
||||
}
|
||||
|
||||
func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
if x.outUsed == len(x.fre) {
|
||||
x.b.Encrypt(x.fre, x.fre)
|
||||
x.outUsed = 0
|
||||
}
|
||||
|
||||
x.fre[x.outUsed] ^= src[i]
|
||||
dst[i] = x.fre[x.outUsed]
|
||||
x.outUsed++
|
||||
}
|
||||
}
|
||||
|
||||
type ocfbDecrypter struct {
|
||||
b cipher.Block
|
||||
fre []byte
|
||||
outUsed int
|
||||
}
|
||||
|
||||
// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's
|
||||
// cipher feedback mode using the given cipher.Block. Prefix must be the first
|
||||
// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's
|
||||
// block size. On successful exit, blockSize+2 bytes of decrypted data are written into
|
||||
// prefix. Resync determines if the "resynchronization step" from RFC 4880,
|
||||
// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point.
|
||||
func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream {
|
||||
blockSize := block.BlockSize()
|
||||
if len(prefix) != blockSize+2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
x := &ocfbDecrypter{
|
||||
b: block,
|
||||
fre: make([]byte, blockSize),
|
||||
outUsed: 0,
|
||||
}
|
||||
prefixCopy := make([]byte, len(prefix))
|
||||
copy(prefixCopy, prefix)
|
||||
|
||||
block.Encrypt(x.fre, x.fre)
|
||||
for i := 0; i < blockSize; i++ {
|
||||
prefixCopy[i] ^= x.fre[i]
|
||||
}
|
||||
|
||||
block.Encrypt(x.fre, prefix[:blockSize])
|
||||
prefixCopy[blockSize] ^= x.fre[0]
|
||||
prefixCopy[blockSize+1] ^= x.fre[1]
|
||||
|
||||
if resync {
|
||||
block.Encrypt(x.fre, prefix[2:])
|
||||
} else {
|
||||
x.fre[0] = prefix[blockSize]
|
||||
x.fre[1] = prefix[blockSize+1]
|
||||
x.outUsed = 2
|
||||
}
|
||||
copy(prefix, prefixCopy)
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
if x.outUsed == len(x.fre) {
|
||||
x.b.Encrypt(x.fre, x.fre)
|
||||
x.outUsed = 0
|
||||
}
|
||||
|
||||
c := src[i]
|
||||
dst[i] = x.fre[x.outUsed] ^ src[i]
|
||||
x.fre[x.outUsed] = c
|
||||
x.outUsed++
|
||||
}
|
||||
}
|
157
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
generated
vendored
Normal file
157
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
|
||||
// section 5.4.
|
||||
type OnePassSignature struct {
|
||||
Version int
|
||||
SigType SignatureType
|
||||
Hash crypto.Hash
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
KeyId uint64
|
||||
IsLast bool
|
||||
Salt []byte // v6 only
|
||||
KeyFingerprint []byte // v6 only
|
||||
}
|
||||
|
||||
func (ops *OnePassSignature) parse(r io.Reader) (err error) {
|
||||
var buf [8]byte
|
||||
// Read: version | signature type | hash algorithm | public-key algorithm
|
||||
_, err = readFull(r, buf[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 3 && buf[0] != 6 {
|
||||
return errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
ops.Version = int(buf[0])
|
||||
|
||||
var ok bool
|
||||
ops.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2])
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2])))
|
||||
}
|
||||
|
||||
ops.SigType = SignatureType(buf[1])
|
||||
ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3])
|
||||
|
||||
if ops.Version == 6 {
|
||||
// Only for v6, a variable-length field containing the salt
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
saltLength := int(buf[0])
|
||||
var expectedSaltLength int
|
||||
expectedSaltLength, err = SaltLengthForHash(ops.Hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if saltLength != expectedSaltLength {
|
||||
err = errors.StructuralError("unexpected salt size for the given hash algorithm")
|
||||
return
|
||||
}
|
||||
salt := make([]byte, expectedSaltLength)
|
||||
_, err = readFull(r, salt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.Salt = salt
|
||||
|
||||
// Only for v6 packets, 32 octets of the fingerprint of the signing key.
|
||||
fingerprint := make([]byte, 32)
|
||||
_, err = readFull(r, fingerprint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.KeyFingerprint = fingerprint
|
||||
ops.KeyId = binary.BigEndian.Uint64(ops.KeyFingerprint[:8])
|
||||
} else {
|
||||
_, err = readFull(r, buf[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.KeyId = binary.BigEndian.Uint64(buf[:8])
|
||||
}
|
||||
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.IsLast = buf[0] != 0
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the given OnePassSignature to w.
|
||||
func (ops *OnePassSignature) Serialize(w io.Writer) error {
|
||||
//v3 length 1+1+1+1+8+1 =
|
||||
packetLength := 13
|
||||
if ops.Version == 6 {
|
||||
// v6 length 1+1+1+1+1+len(salt)+32+1 =
|
||||
packetLength = 38 + len(ops.Salt)
|
||||
}
|
||||
|
||||
if err := serializeHeader(w, packetTypeOnePassSignature, packetLength); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf [8]byte
|
||||
buf[0] = byte(ops.Version)
|
||||
buf[1] = uint8(ops.SigType)
|
||||
var ok bool
|
||||
buf[2], ok = algorithm.HashToHashIdWithSha1(ops.Hash)
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash)))
|
||||
}
|
||||
buf[3] = uint8(ops.PubKeyAlgo)
|
||||
|
||||
_, err := w.Write(buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ops.Version == 6 {
|
||||
// write salt for v6 signatures
|
||||
_, err := w.Write([]byte{uint8(len(ops.Salt))})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(ops.Salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write fingerprint v6 signatures
|
||||
_, err = w.Write(ops.KeyFingerprint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
binary.BigEndian.PutUint64(buf[:8], ops.KeyId)
|
||||
_, err := w.Write(buf[:8])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
isLast := []byte{byte(0)}
|
||||
if ops.IsLast {
|
||||
isLast[0] = 1
|
||||
}
|
||||
|
||||
_, err = w.Write(isLast)
|
||||
return err
|
||||
}
|
170
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
generated
vendored
Normal file
170
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright 2012 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is
|
||||
// useful for splitting and storing the original packet contents separately,
|
||||
// handling unsupported packet types or accessing parts of the packet not yet
|
||||
// implemented by this package.
|
||||
type OpaquePacket struct {
|
||||
// Packet type
|
||||
Tag uint8
|
||||
// Reason why the packet was parsed opaquely
|
||||
Reason error
|
||||
// Binary contents of the packet data
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
func (op *OpaquePacket) parse(r io.Reader) (err error) {
|
||||
op.Contents, err = io.ReadAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the packet to a writer in its original form, including
|
||||
// the packet header.
|
||||
func (op *OpaquePacket) Serialize(w io.Writer) (err error) {
|
||||
err = serializeHeader(w, packetType(op.Tag), len(op.Contents))
|
||||
if err == nil {
|
||||
_, err = w.Write(op.Contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Parse attempts to parse the opaque contents into a structure supported by
|
||||
// this package. If the packet is not known then the result will be another
|
||||
// OpaquePacket.
|
||||
func (op *OpaquePacket) Parse() (p Packet, err error) {
|
||||
hdr := bytes.NewBuffer(nil)
|
||||
err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents))
|
||||
if err != nil {
|
||||
op.Reason = err
|
||||
return op, err
|
||||
}
|
||||
p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents)))
|
||||
if err != nil {
|
||||
op.Reason = err
|
||||
p = op
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpaqueReader reads OpaquePackets from an io.Reader.
|
||||
type OpaqueReader struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func NewOpaqueReader(r io.Reader) *OpaqueReader {
|
||||
return &OpaqueReader{r: r}
|
||||
}
|
||||
|
||||
// Read the next OpaquePacket.
|
||||
func (or *OpaqueReader) Next() (op *OpaquePacket, err error) {
|
||||
tag, _, contents, err := readHeader(or.r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
op = &OpaquePacket{Tag: uint8(tag), Reason: err}
|
||||
err = op.parse(contents)
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpaqueSubpacket represents an unparsed OpenPGP subpacket,
|
||||
// as found in signature and user attribute packets.
|
||||
type OpaqueSubpacket struct {
|
||||
SubType uint8
|
||||
EncodedLength []byte // Store the original encoded length for signature verifications.
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from
|
||||
// their byte representation.
|
||||
func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) {
|
||||
var (
|
||||
subHeaderLen int
|
||||
subPacket *OpaqueSubpacket
|
||||
)
|
||||
for len(contents) > 0 {
|
||||
subHeaderLen, subPacket, err = nextSubpacket(contents)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
result = append(result, subPacket)
|
||||
contents = contents[subHeaderLen+len(subPacket.Contents):]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) {
|
||||
// RFC 4880, section 5.2.3.1
|
||||
var subLen uint32
|
||||
var encodedLength []byte
|
||||
if len(contents) < 1 {
|
||||
goto Truncated
|
||||
}
|
||||
subPacket = &OpaqueSubpacket{}
|
||||
switch {
|
||||
case contents[0] < 192:
|
||||
subHeaderLen = 2 // 1 length byte, 1 subtype byte
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
encodedLength = contents[0:1]
|
||||
subLen = uint32(contents[0])
|
||||
contents = contents[1:]
|
||||
case contents[0] < 255:
|
||||
subHeaderLen = 3 // 2 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
encodedLength = contents[0:2]
|
||||
subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192
|
||||
contents = contents[2:]
|
||||
default:
|
||||
subHeaderLen = 6 // 5 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
encodedLength = contents[0:5]
|
||||
subLen = uint32(contents[1])<<24 |
|
||||
uint32(contents[2])<<16 |
|
||||
uint32(contents[3])<<8 |
|
||||
uint32(contents[4])
|
||||
contents = contents[5:]
|
||||
|
||||
}
|
||||
if subLen > uint32(len(contents)) || subLen == 0 {
|
||||
goto Truncated
|
||||
}
|
||||
subPacket.SubType = contents[0]
|
||||
subPacket.EncodedLength = encodedLength
|
||||
subPacket.Contents = contents[1:subLen]
|
||||
return
|
||||
Truncated:
|
||||
err = errors.StructuralError("subpacket truncated")
|
||||
return
|
||||
}
|
||||
|
||||
func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) {
|
||||
buf := make([]byte, 6)
|
||||
copy(buf, osp.EncodedLength)
|
||||
n := len(osp.EncodedLength)
|
||||
|
||||
buf[n] = osp.SubType
|
||||
if _, err = w.Write(buf[:n+1]); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(osp.Contents)
|
||||
return
|
||||
}
|
675
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
generated
vendored
Normal file
675
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
generated
vendored
Normal file
@ -0,0 +1,675 @@
|
||||
// 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.
|
||||
package packet // import "github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// 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
|
||||
buf bytes.Buffer
|
||||
lengthByte [1]byte
|
||||
}
|
||||
|
||||
func (w *partialLengthWriter) Write(p []byte) (n int, err error) {
|
||||
bufLen := w.buf.Len()
|
||||
if bufLen > 512 {
|
||||
for power := uint(30); ; power-- {
|
||||
l := 1 << power
|
||||
if bufLen >= l {
|
||||
w.lengthByte[0] = 224 + uint8(power)
|
||||
_, err = w.w.Write(w.lengthByte[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var m int
|
||||
m, err = w.w.Write(w.buf.Next(l))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if m != l {
|
||||
return 0, io.ErrShortWrite
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return w.buf.Write(p)
|
||||
}
|
||||
|
||||
func (w *partialLengthWriter) Close() (err error) {
|
||||
len := w.buf.Len()
|
||||
err = serializeLength(w.w, len)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.buf.WriteTo(w.w)
|
||||
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) {
|
||||
err = serializeType(w, ptype)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return serializeLength(w, length)
|
||||
}
|
||||
|
||||
// serializeType writes an OpenPGP packet type to w. See RFC 4880, section
|
||||
// 4.2.
|
||||
func serializeType(w io.Writer, ptype packetType) (err error) {
|
||||
var buf [1]byte
|
||||
buf[0] = 0x80 | 0x40 | byte(ptype)
|
||||
_, err = w.Write(buf[:])
|
||||
return
|
||||
}
|
||||
|
||||
// serializeLength writes an OpenPGP packet length to w. See RFC 4880, section
|
||||
// 4.2.2.
|
||||
func serializeLength(w io.Writer, length int) (err error) {
|
||||
var buf [5]byte
|
||||
var n int
|
||||
|
||||
if length < 192 {
|
||||
buf[0] = byte(length)
|
||||
n = 1
|
||||
} else if length < 8384 {
|
||||
length -= 192
|
||||
buf[0] = 192 + byte(length>>8)
|
||||
buf[1] = byte(length)
|
||||
n = 2
|
||||
} else {
|
||||
buf[0] = 255
|
||||
buf[1] = byte(length >> 24)
|
||||
buf[2] = byte(length >> 16)
|
||||
buf[3] = byte(length >> 8)
|
||||
buf[4] = byte(length)
|
||||
n = 5
|
||||
}
|
||||
|
||||
_, 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) {
|
||||
err = serializeType(w, ptype)
|
||||
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
|
||||
packetTypeMarker packetType = 10
|
||||
packetTypeLiteralData packetType = 11
|
||||
packetTypeTrust packetType = 12
|
||||
packetTypeUserId packetType = 13
|
||||
packetTypePublicSubkey packetType = 14
|
||||
packetTypeUserAttribute packetType = 17
|
||||
packetTypeSymmetricallyEncryptedIntegrityProtected packetType = 18
|
||||
packetTypeAEADEncrypted packetType = 20
|
||||
packetPadding packetType = 21
|
||||
)
|
||||
|
||||
// EncryptedDataPacket holds encrypted data. It is currently implemented by
|
||||
// SymmetricallyEncrypted and AEADEncrypted.
|
||||
type EncryptedDataPacket interface {
|
||||
Decrypt(CipherFunction, []byte) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// 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, len, contents, err := readHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case packetTypeEncryptedKey:
|
||||
p = new(EncryptedKey)
|
||||
case packetTypeSignature:
|
||||
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:
|
||||
isSubkey := tag == packetTypePublicSubkey
|
||||
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 packetTypeSymmetricallyEncryptedIntegrityProtected:
|
||||
se := new(SymmetricallyEncrypted)
|
||||
se.IntegrityProtected = true
|
||||
p = se
|
||||
case packetTypeAEADEncrypted:
|
||||
p = new(AEADEncrypted)
|
||||
case packetPadding:
|
||||
p = Padding(len)
|
||||
case packetTypeMarker:
|
||||
p = new(Marker)
|
||||
case packetTypeTrust:
|
||||
// Not implemented, just consume
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
default:
|
||||
// Packet Tags from 0 to 39 are critical.
|
||||
// Packet Tags from 40 to 63 are non-critical.
|
||||
if tag < 40 {
|
||||
err = errors.CriticalUnknownPacketTypeError(tag)
|
||||
} else {
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
}
|
||||
}
|
||||
if p != nil {
|
||||
err = p.parse(contents)
|
||||
}
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadWithCheck reads a single OpenPGP message packet from the given io.Reader. If there is an
|
||||
// error parsing a packet, the whole packet is consumed from the input.
|
||||
// ReadWithCheck additionally checks if the OpenPGP message packet sequence adheres
|
||||
// to the packet composition rules in rfc4880, if not throws an error.
|
||||
func ReadWithCheck(r io.Reader, sequence *SequenceVerifier) (p Packet, msgErr error, err error) {
|
||||
tag, len, contents, err := readHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch tag {
|
||||
case packetTypeEncryptedKey:
|
||||
msgErr = sequence.Next(ESKSymbol)
|
||||
p = new(EncryptedKey)
|
||||
case packetTypeSignature:
|
||||
msgErr = sequence.Next(SigSymbol)
|
||||
p = new(Signature)
|
||||
case packetTypeSymmetricKeyEncrypted:
|
||||
msgErr = sequence.Next(ESKSymbol)
|
||||
p = new(SymmetricKeyEncrypted)
|
||||
case packetTypeOnePassSignature:
|
||||
msgErr = sequence.Next(OPSSymbol)
|
||||
p = new(OnePassSignature)
|
||||
case packetTypeCompressed:
|
||||
msgErr = sequence.Next(CompSymbol)
|
||||
p = new(Compressed)
|
||||
case packetTypeSymmetricallyEncrypted:
|
||||
msgErr = sequence.Next(EncSymbol)
|
||||
p = new(SymmetricallyEncrypted)
|
||||
case packetTypeLiteralData:
|
||||
msgErr = sequence.Next(LDSymbol)
|
||||
p = new(LiteralData)
|
||||
case packetTypeSymmetricallyEncryptedIntegrityProtected:
|
||||
msgErr = sequence.Next(EncSymbol)
|
||||
se := new(SymmetricallyEncrypted)
|
||||
se.IntegrityProtected = true
|
||||
p = se
|
||||
case packetTypeAEADEncrypted:
|
||||
msgErr = sequence.Next(EncSymbol)
|
||||
p = new(AEADEncrypted)
|
||||
case packetPadding:
|
||||
p = Padding(len)
|
||||
case packetTypeMarker:
|
||||
p = new(Marker)
|
||||
case packetTypeTrust:
|
||||
// Not implemented, just consume
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
case packetTypePrivateKey,
|
||||
packetTypePrivateSubkey,
|
||||
packetTypePublicKey,
|
||||
packetTypePublicSubkey,
|
||||
packetTypeUserId,
|
||||
packetTypeUserAttribute:
|
||||
msgErr = sequence.Next(UnknownSymbol)
|
||||
consumeAll(contents)
|
||||
default:
|
||||
// Packet Tags from 0 to 39 are critical.
|
||||
// Packet Tags from 40 to 63 are non-critical.
|
||||
if tag < 40 {
|
||||
err = errors.CriticalUnknownPacketTypeError(tag)
|
||||
} else {
|
||||
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 = 0x00
|
||||
SigTypeText SignatureType = 0x01
|
||||
SigTypeGenericCert SignatureType = 0x10
|
||||
SigTypePersonaCert SignatureType = 0x11
|
||||
SigTypeCasualCert SignatureType = 0x12
|
||||
SigTypePositiveCert SignatureType = 0x13
|
||||
SigTypeSubkeyBinding SignatureType = 0x18
|
||||
SigTypePrimaryKeyBinding SignatureType = 0x19
|
||||
SigTypeDirectSignature SignatureType = 0x1F
|
||||
SigTypeKeyRevocation SignatureType = 0x20
|
||||
SigTypeSubkeyRevocation SignatureType = 0x28
|
||||
SigTypeCertificationRevocation SignatureType = 0x30
|
||||
)
|
||||
|
||||
// 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
|
||||
// https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt
|
||||
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh
|
||||
PubKeyAlgoX25519 PublicKeyAlgorithm = 25
|
||||
PubKeyAlgoX448 PublicKeyAlgorithm = 26
|
||||
PubKeyAlgoEd25519 PublicKeyAlgorithm = 27
|
||||
PubKeyAlgoEd448 PublicKeyAlgorithm = 28
|
||||
|
||||
// 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, PubKeyAlgoECDH, PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
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, PubKeyAlgoEdDSA, PubKeyAlgoEd25519, PubKeyAlgoEd448:
|
||||
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 algorithm.CipherFunction
|
||||
|
||||
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 {
|
||||
return algorithm.CipherFunction(cipher).KeySize()
|
||||
}
|
||||
|
||||
// IsSupported returns true if the cipher is supported from the library
|
||||
func (cipher CipherFunction) IsSupported() bool {
|
||||
return algorithm.CipherFunction(cipher).KeySize() > 0
|
||||
}
|
||||
|
||||
// blockSize returns the block size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) blockSize() int {
|
||||
return algorithm.CipherFunction(cipher).BlockSize()
|
||||
}
|
||||
|
||||
// new returns a fresh instance of the given cipher.
|
||||
func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
|
||||
return algorithm.CipherFunction(cipher).New(key)
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// AEADMode represents the different Authenticated Encryption with Associated
|
||||
// Data specified for OpenPGP.
|
||||
// See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.6
|
||||
type AEADMode algorithm.AEADMode
|
||||
|
||||
const (
|
||||
AEADModeEAX AEADMode = 1
|
||||
AEADModeOCB AEADMode = 2
|
||||
AEADModeGCM AEADMode = 3
|
||||
)
|
||||
|
||||
func (mode AEADMode) IvLength() int {
|
||||
return algorithm.AEADMode(mode).NonceLength()
|
||||
}
|
||||
|
||||
func (mode AEADMode) TagLength() int {
|
||||
return algorithm.AEADMode(mode).TagLength()
|
||||
}
|
||||
|
||||
// IsSupported returns true if the aead mode is supported from the library
|
||||
func (mode AEADMode) IsSupported() bool {
|
||||
return algorithm.AEADMode(mode).TagLength() > 0
|
||||
}
|
||||
|
||||
// new returns a fresh instance of the given mode.
|
||||
func (mode AEADMode) new(block cipher.Block) cipher.AEAD {
|
||||
return algorithm.AEADMode(mode).New(block)
|
||||
}
|
||||
|
||||
// ReasonForRevocation represents a revocation reason code as per RFC4880
|
||||
// section 5.2.3.23.
|
||||
type ReasonForRevocation uint8
|
||||
|
||||
const (
|
||||
NoReason ReasonForRevocation = 0
|
||||
KeySuperseded ReasonForRevocation = 1
|
||||
KeyCompromised ReasonForRevocation = 2
|
||||
KeyRetired ReasonForRevocation = 3
|
||||
UserIDNotValid ReasonForRevocation = 32
|
||||
Unknown ReasonForRevocation = 200
|
||||
)
|
||||
|
||||
func NewReasonForRevocation(value byte) ReasonForRevocation {
|
||||
if value < 4 || value == 32 {
|
||||
return ReasonForRevocation(value)
|
||||
}
|
||||
return Unknown
|
||||
}
|
||||
|
||||
// Curve is a mapping to supported ECC curves for key generation.
|
||||
// See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-06.html#name-curve-specific-wire-formats
|
||||
type Curve string
|
||||
|
||||
const (
|
||||
Curve25519 Curve = "Curve25519"
|
||||
Curve448 Curve = "Curve448"
|
||||
CurveNistP256 Curve = "P256"
|
||||
CurveNistP384 Curve = "P384"
|
||||
CurveNistP521 Curve = "P521"
|
||||
CurveSecP256k1 Curve = "SecP256k1"
|
||||
CurveBrainpoolP256 Curve = "BrainpoolP256"
|
||||
CurveBrainpoolP384 Curve = "BrainpoolP384"
|
||||
CurveBrainpoolP512 Curve = "BrainpoolP512"
|
||||
)
|
||||
|
||||
// TrustLevel represents a trust level per RFC4880 5.2.3.13
|
||||
type TrustLevel uint8
|
||||
|
||||
// TrustAmount represents a trust amount per RFC4880 5.2.3.13
|
||||
type TrustAmount uint8
|
||||
|
||||
const (
|
||||
// versionSize is the length in bytes of the version value.
|
||||
versionSize = 1
|
||||
// algorithmSize is the length in bytes of the key algorithm value.
|
||||
algorithmSize = 1
|
||||
// keyVersionSize is the length in bytes of the key version value
|
||||
keyVersionSize = 1
|
||||
// keyIdSize is the length in bytes of the key identifier value.
|
||||
keyIdSize = 8
|
||||
// timestampSize is the length in bytes of encoded timestamps.
|
||||
timestampSize = 4
|
||||
// fingerprintSizeV6 is the length in bytes of the key fingerprint in v6.
|
||||
fingerprintSizeV6 = 32
|
||||
// fingerprintSize is the length in bytes of the key fingerprint.
|
||||
fingerprintSize = 20
|
||||
)
|
222
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go
generated
vendored
Normal file
222
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
package packet
|
||||
|
||||
// This file implements the pushdown automata (PDA) from PGPainless (Paul Schaub)
|
||||
// to verify pgp packet sequences. See Paul's blogpost for more details:
|
||||
// https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
func NewErrMalformedMessage(from State, input InputSymbol, stackSymbol StackSymbol) errors.ErrMalformedMessage {
|
||||
return errors.ErrMalformedMessage(fmt.Sprintf("state %d, input symbol %d, stack symbol %d ", from, input, stackSymbol))
|
||||
}
|
||||
|
||||
// InputSymbol defines the input alphabet of the PDA
|
||||
type InputSymbol uint8
|
||||
|
||||
const (
|
||||
LDSymbol InputSymbol = iota
|
||||
SigSymbol
|
||||
OPSSymbol
|
||||
CompSymbol
|
||||
ESKSymbol
|
||||
EncSymbol
|
||||
EOSSymbol
|
||||
UnknownSymbol
|
||||
)
|
||||
|
||||
// StackSymbol defines the stack alphabet of the PDA
|
||||
type StackSymbol int8
|
||||
|
||||
const (
|
||||
MsgStackSymbol StackSymbol = iota
|
||||
OpsStackSymbol
|
||||
KeyStackSymbol
|
||||
EndStackSymbol
|
||||
EmptyStackSymbol
|
||||
)
|
||||
|
||||
// State defines the states of the PDA
|
||||
type State int8
|
||||
|
||||
const (
|
||||
OpenPGPMessage State = iota
|
||||
ESKMessage
|
||||
LiteralMessage
|
||||
CompressedMessage
|
||||
EncryptedMessage
|
||||
ValidMessage
|
||||
)
|
||||
|
||||
// transition represents a state transition in the PDA
|
||||
type transition func(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error)
|
||||
|
||||
// SequenceVerifier is a pushdown automata to verify
|
||||
// PGP messages packet sequences according to rfc4880.
|
||||
type SequenceVerifier struct {
|
||||
stack []StackSymbol
|
||||
state State
|
||||
}
|
||||
|
||||
// Next performs a state transition with the given input symbol.
|
||||
// If the transition fails a ErrMalformedMessage is returned.
|
||||
func (sv *SequenceVerifier) Next(input InputSymbol) error {
|
||||
for {
|
||||
stackSymbol := sv.popStack()
|
||||
transitionFunc := getTransition(sv.state)
|
||||
nextState, newStackSymbols, redo, err := transitionFunc(input, stackSymbol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if redo {
|
||||
sv.pushStack(stackSymbol)
|
||||
}
|
||||
for _, newStackSymbol := range newStackSymbols {
|
||||
sv.pushStack(newStackSymbol)
|
||||
}
|
||||
sv.state = nextState
|
||||
if !redo {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Valid returns true if RDA is in a valid state.
|
||||
func (sv *SequenceVerifier) Valid() bool {
|
||||
return sv.state == ValidMessage && len(sv.stack) == 0
|
||||
}
|
||||
|
||||
func (sv *SequenceVerifier) AssertValid() error {
|
||||
if !sv.Valid() {
|
||||
return errors.ErrMalformedMessage("invalid message")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSequenceVerifier() *SequenceVerifier {
|
||||
return &SequenceVerifier{
|
||||
stack: []StackSymbol{EndStackSymbol, MsgStackSymbol},
|
||||
state: OpenPGPMessage,
|
||||
}
|
||||
}
|
||||
|
||||
func (sv *SequenceVerifier) popStack() StackSymbol {
|
||||
if len(sv.stack) == 0 {
|
||||
return EmptyStackSymbol
|
||||
}
|
||||
elemIndex := len(sv.stack) - 1
|
||||
stackSymbol := sv.stack[elemIndex]
|
||||
sv.stack = sv.stack[:elemIndex]
|
||||
return stackSymbol
|
||||
}
|
||||
|
||||
func (sv *SequenceVerifier) pushStack(stackSymbol StackSymbol) {
|
||||
sv.stack = append(sv.stack, stackSymbol)
|
||||
}
|
||||
|
||||
func getTransition(from State) transition {
|
||||
switch from {
|
||||
case OpenPGPMessage:
|
||||
return fromOpenPGPMessage
|
||||
case LiteralMessage:
|
||||
return fromLiteralMessage
|
||||
case CompressedMessage:
|
||||
return fromCompressedMessage
|
||||
case EncryptedMessage:
|
||||
return fromEncryptedMessage
|
||||
case ESKMessage:
|
||||
return fromESKMessage
|
||||
case ValidMessage:
|
||||
return fromValidMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fromOpenPGPMessage is the transition for the state OpenPGPMessage.
|
||||
func fromOpenPGPMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
if stackSymbol != MsgStackSymbol {
|
||||
return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol)
|
||||
}
|
||||
switch input {
|
||||
case LDSymbol:
|
||||
return LiteralMessage, nil, false, nil
|
||||
case SigSymbol:
|
||||
return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, false, nil
|
||||
case OPSSymbol:
|
||||
return OpenPGPMessage, []StackSymbol{OpsStackSymbol, MsgStackSymbol}, false, nil
|
||||
case CompSymbol:
|
||||
return CompressedMessage, nil, false, nil
|
||||
case ESKSymbol:
|
||||
return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil
|
||||
case EncSymbol:
|
||||
return EncryptedMessage, nil, false, nil
|
||||
}
|
||||
return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol)
|
||||
}
|
||||
|
||||
// fromESKMessage is the transition for the state ESKMessage.
|
||||
func fromESKMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
if stackSymbol != KeyStackSymbol {
|
||||
return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol)
|
||||
}
|
||||
switch input {
|
||||
case ESKSymbol:
|
||||
return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil
|
||||
case EncSymbol:
|
||||
return EncryptedMessage, nil, false, nil
|
||||
}
|
||||
return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol)
|
||||
}
|
||||
|
||||
// fromLiteralMessage is the transition for the state LiteralMessage.
|
||||
func fromLiteralMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
switch input {
|
||||
case SigSymbol:
|
||||
if stackSymbol == OpsStackSymbol {
|
||||
return LiteralMessage, nil, false, nil
|
||||
}
|
||||
case EOSSymbol:
|
||||
if stackSymbol == EndStackSymbol {
|
||||
return ValidMessage, nil, false, nil
|
||||
}
|
||||
}
|
||||
return 0, nil, false, NewErrMalformedMessage(LiteralMessage, input, stackSymbol)
|
||||
}
|
||||
|
||||
// fromLiteralMessage is the transition for the state CompressedMessage.
|
||||
func fromCompressedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
switch input {
|
||||
case SigSymbol:
|
||||
if stackSymbol == OpsStackSymbol {
|
||||
return CompressedMessage, nil, false, nil
|
||||
}
|
||||
case EOSSymbol:
|
||||
if stackSymbol == EndStackSymbol {
|
||||
return ValidMessage, nil, false, nil
|
||||
}
|
||||
}
|
||||
return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil
|
||||
}
|
||||
|
||||
// fromEncryptedMessage is the transition for the state EncryptedMessage.
|
||||
func fromEncryptedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
switch input {
|
||||
case SigSymbol:
|
||||
if stackSymbol == OpsStackSymbol {
|
||||
return EncryptedMessage, nil, false, nil
|
||||
}
|
||||
case EOSSymbol:
|
||||
if stackSymbol == EndStackSymbol {
|
||||
return ValidMessage, nil, false, nil
|
||||
}
|
||||
}
|
||||
return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil
|
||||
}
|
||||
|
||||
// fromValidMessage is the transition for the state ValidMessage.
|
||||
func fromValidMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
return 0, nil, false, NewErrMalformedMessage(ValidMessage, input, stackSymbol)
|
||||
}
|
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go
generated
vendored
Normal file
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// UnsupportedPackage represents a OpenPGP packet with a known packet type
|
||||
// but with unsupported content.
|
||||
type UnsupportedPacket struct {
|
||||
IncompletePacket Packet
|
||||
Error errors.UnsupportedError
|
||||
}
|
||||
|
||||
// Implements the Packet interface
|
||||
func (up *UnsupportedPacket) parse(read io.Reader) error {
|
||||
err := up.IncompletePacket.parse(read)
|
||||
if castedErr, ok := err.(errors.UnsupportedError); ok {
|
||||
up.Error = castedErr
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
26
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go
generated
vendored
Normal file
26
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Padding type represents a Padding Packet (Tag 21).
|
||||
// The padding type is represented by the length of its padding.
|
||||
// see https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21
|
||||
type Padding int
|
||||
|
||||
// parse just ignores the padding content.
|
||||
func (pad Padding) parse(reader io.Reader) error {
|
||||
_, err := io.CopyN(io.Discard, reader, int64(pad))
|
||||
return err
|
||||
}
|
||||
|
||||
// SerializePadding writes the padding to writer.
|
||||
func (pad Padding) SerializePadding(writer io.Writer, rand io.Reader) error {
|
||||
err := serializeHeader(writer, packetPadding, int(pad))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.CopyN(writer, rand, int64(pad))
|
||||
return err
|
||||
}
|
1191
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
generated
vendored
Normal file
1191
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go
generated
vendored
Normal file
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package packet
|
||||
|
||||
// Generated with `gpg --export-secret-keys "Test Key 2"`
|
||||
const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec"
|
||||
|
||||
// Generated by `gpg --export-secret-keys` followed by a manual extraction of
|
||||
// the ElGamal subkey from the packets.
|
||||
const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc"
|
||||
|
||||
// pkcs1PrivKeyHex is a PKCS#1, RSA private key.
|
||||
// Generated by `openssl genrsa 1024 | openssl rsa -outform DER | xxd -p`
|
||||
const pkcs1PrivKeyHex = "3082025d02010002818100e98edfa1c3b35884a54d0b36a6a603b0290fa85e49e30fa23fc94fef9c6790bc4849928607aa48d809da326fb42a969d06ad756b98b9c1a90f5d4a2b6d0ac05953c97f4da3120164a21a679793ce181c906dc01d235cc085ddcdf6ea06c389b6ab8885dfd685959e693138856a68a7e5db263337ff82a088d583a897cf2d59e9020301000102818100b6d5c9eb70b02d5369b3ee5b520a14490b5bde8a317d36f7e4c74b7460141311d1e5067735f8f01d6f5908b2b96fbd881f7a1ab9a84d82753e39e19e2d36856be960d05ac9ef8e8782ea1b6d65aee28fdfe1d61451e8cff0adfe84322f12cf455028b581cf60eb9e0e140ba5d21aeba6c2634d7c65318b9a665fc01c3191ca21024100fa5e818da3705b0fa33278bb28d4b6f6050388af2d4b75ec9375dd91ccf2e7d7068086a8b82a8f6282e4fbbdb8a7f2622eb97295249d87acea7f5f816f54d347024100eecf9406d7dc49cdfb95ab1eff4064de84c7a30f64b2798936a0d2018ba9eb52e4b636f82e96c49cc63b80b675e91e40d1b2e4017d4b9adaf33ab3d9cf1c214f024100c173704ace742c082323066226a4655226819a85304c542b9dacbeacbf5d1881ee863485fcf6f59f3a604f9b42289282067447f2b13dfeed3eab7851fc81e0550240741fc41f3fc002b382eed8730e33c5d8de40256e4accee846667f536832f711ab1d4590e7db91a8a116ac5bff3be13d3f9243ff2e976662aa9b395d907f8e9c9024046a5696c9ef882363e06c9fa4e2f5b580906452befba03f4a99d0f873697ef1f851d2226ca7934b30b7c3e80cb634a67172bbbf4781735fe3e09263e2dd723e7"
|
1125
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
Normal file
1125
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go
generated
vendored
Normal file
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package packet
|
||||
|
||||
const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
|
||||
|
||||
const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"
|
||||
|
||||
const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed"
|
||||
|
||||
const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0"
|
||||
|
||||
const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b"
|
||||
|
||||
const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4"
|
||||
|
||||
const ecdhFingerprintHex = "722354df2475a42164d1d49faa8b938f9a201946"
|
||||
|
||||
const ecdhPkDataHex = "b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec91803010909"
|
||||
|
||||
const eddsaFingerprintHex = "b2d5e5ec0e6deca6bc8eeeb00907e75e1dd99ad8"
|
||||
|
||||
const eddsaPkDataHex = "98330456e2132b16092b06010401da470f01010740bbda39266affa511a8c2d02edf690fb784b0499c4406185811a163539ef11dc1b41d74657374696e67203c74657374696e674074657374696e672e636f6d3e8879041316080021050256e2132b021b03050b09080702061508090a0b020416020301021e01021780000a09100907e75e1dd99ad86d0c00fe39d2008359352782bc9b61ac382584cd8eff3f57a18c2287e3afeeb05d1f04ba00fe2d0bc1ddf3ff8adb9afa3e7d9287244b4ec567f3db4d60b74a9b5465ed528203"
|
||||
|
||||
// Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key
|
||||
const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267`
|
209
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
generated
vendored
Normal file
209
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
generated
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
type PacketReader interface {
|
||||
Next() (p Packet, err error)
|
||||
Push(reader io.Reader) (err error)
|
||||
Unread(p Packet)
|
||||
}
|
||||
|
||||
// Reader reads packets from an io.Reader and allows packets to be 'unread' so
|
||||
// that they result from the next call to Next.
|
||||
type Reader struct {
|
||||
q []Packet
|
||||
readers []io.Reader
|
||||
}
|
||||
|
||||
// New io.Readers are pushed when a compressed or encrypted packet is processed
|
||||
// and recursively treated as a new source of packets. However, a carefully
|
||||
// crafted packet can trigger an infinite recursive sequence of packets. See
|
||||
// http://mumble.net/~campbell/misc/pgp-quine
|
||||
// https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4402
|
||||
// This constant limits the number of recursive packets that may be pushed.
|
||||
const maxReaders = 32
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown/unsupported/Marker packet types are skipped.
|
||||
func (r *Reader) Next() (p Packet, err error) {
|
||||
for {
|
||||
p, err := r.read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
switch p.(type) {
|
||||
case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
} else {
|
||||
//A marker packet MUST be ignored when received
|
||||
switch p.(type) {
|
||||
case *Marker:
|
||||
continue
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown/Marker packet types are skipped while unsupported
|
||||
// packets are returned as UnsupportedPacket type.
|
||||
func (r *Reader) NextWithUnsupported() (p Packet, err error) {
|
||||
for {
|
||||
p, err = r.read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if casteErr, ok := err.(errors.UnsupportedError); ok {
|
||||
return &UnsupportedPacket{
|
||||
IncompletePacket: p,
|
||||
Error: casteErr,
|
||||
}, nil
|
||||
}
|
||||
return
|
||||
} else {
|
||||
//A marker packet MUST be ignored when received
|
||||
switch p.(type) {
|
||||
case *Marker:
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (r *Reader) read() (p Packet, err error) {
|
||||
if len(r.q) > 0 {
|
||||
p = r.q[len(r.q)-1]
|
||||
r.q = r.q[:len(r.q)-1]
|
||||
return
|
||||
}
|
||||
for len(r.readers) > 0 {
|
||||
p, err = Read(r.readers[len(r.readers)-1])
|
||||
if err == io.EOF {
|
||||
r.readers = r.readers[:len(r.readers)-1]
|
||||
continue
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Push causes the Reader to start reading from a new io.Reader. When an EOF
|
||||
// error is seen from the new io.Reader, it is popped and the Reader continues
|
||||
// to read from the next most recent io.Reader. Push returns a StructuralError
|
||||
// if pushing the reader would exceed the maximum recursion level, otherwise it
|
||||
// returns nil.
|
||||
func (r *Reader) Push(reader io.Reader) (err error) {
|
||||
if len(r.readers) >= maxReaders {
|
||||
return errors.StructuralError("too many layers of packets")
|
||||
}
|
||||
r.readers = append(r.readers, reader)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unread causes the given Packet to be returned from the next call to Next.
|
||||
func (r *Reader) Unread(p Packet) {
|
||||
r.q = append(r.q, p)
|
||||
}
|
||||
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
q: nil,
|
||||
readers: []io.Reader{r},
|
||||
}
|
||||
}
|
||||
|
||||
// CheckReader is similar to Reader but additionally
|
||||
// uses the pushdown automata to verify the read packet sequence.
|
||||
type CheckReader struct {
|
||||
Reader
|
||||
verifier *SequenceVerifier
|
||||
fullyRead bool
|
||||
}
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown packet types are skipped.
|
||||
// If the read packet sequence does not conform to the packet composition
|
||||
// rules in rfc4880, it returns an error.
|
||||
func (r *CheckReader) Next() (p Packet, err error) {
|
||||
if r.fullyRead {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if len(r.q) > 0 {
|
||||
p = r.q[len(r.q)-1]
|
||||
r.q = r.q[:len(r.q)-1]
|
||||
return
|
||||
}
|
||||
var errMsg error
|
||||
for len(r.readers) > 0 {
|
||||
p, errMsg, err = ReadWithCheck(r.readers[len(r.readers)-1], r.verifier)
|
||||
if errMsg != nil {
|
||||
err = errMsg
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == io.EOF {
|
||||
r.readers = r.readers[:len(r.readers)-1]
|
||||
continue
|
||||
}
|
||||
//A marker packet MUST be ignored when received
|
||||
switch p.(type) {
|
||||
case *Marker:
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
switch p.(type) {
|
||||
case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if errMsg = r.verifier.Next(EOSSymbol); errMsg != nil {
|
||||
return nil, errMsg
|
||||
}
|
||||
if errMsg = r.verifier.AssertValid(); errMsg != nil {
|
||||
return nil, errMsg
|
||||
}
|
||||
r.fullyRead = true
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func NewCheckReader(r io.Reader) *CheckReader {
|
||||
return &CheckReader{
|
||||
Reader: Reader{
|
||||
q: nil,
|
||||
readers: []io.Reader{r},
|
||||
},
|
||||
verifier: NewSequenceVerifier(),
|
||||
fullyRead: false,
|
||||
}
|
||||
}
|
15
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go
generated
vendored
Normal file
15
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package packet
|
||||
|
||||
// Recipient type represents a Intended Recipient Fingerprint subpacket
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr
|
||||
type Recipient struct {
|
||||
KeyVersion int
|
||||
Fingerprint []byte
|
||||
}
|
||||
|
||||
func (r *Recipient) Serialize() []byte {
|
||||
packet := make([]byte, len(r.Fingerprint)+1)
|
||||
packet[0] = byte(r.KeyVersion)
|
||||
copy(packet[1:], r.Fingerprint)
|
||||
return packet
|
||||
}
|
1511
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
Normal file
1511
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
331
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
Normal file
331
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
Normal file
@ -0,0 +1,331 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// This is the largest session key that we'll support. Since at most 256-bit cipher
|
||||
// is supported in OpenPGP, this is large enough to contain also the auth tag.
|
||||
const maxSessionKeySizeInBytes = 64
|
||||
|
||||
// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
|
||||
// 4880, section 5.3.
|
||||
type SymmetricKeyEncrypted struct {
|
||||
Version int
|
||||
CipherFunc CipherFunction
|
||||
Mode AEADMode
|
||||
s2k func(out, in []byte)
|
||||
iv []byte
|
||||
encryptedKey []byte // Contains also the authentication tag for AEAD
|
||||
}
|
||||
|
||||
// parse parses an SymmetricKeyEncrypted packet as specified in
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#name-symmetric-key-encrypted-ses
|
||||
func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
||||
var buf [1]byte
|
||||
|
||||
// Version
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
ske.Version = int(buf[0])
|
||||
if ske.Version != 4 && ske.Version != 5 && ske.Version != 6 {
|
||||
return errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
|
||||
}
|
||||
|
||||
if V5Disabled && ske.Version == 5 {
|
||||
return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
|
||||
}
|
||||
|
||||
if ske.Version > 5 {
|
||||
// Scalar octet count
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Cipher function
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
ske.CipherFunc = CipherFunction(buf[0])
|
||||
if !ske.CipherFunc.IsSupported() {
|
||||
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
if ske.Version >= 5 {
|
||||
// AEAD mode
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return errors.StructuralError("cannot read AEAD octet from packet")
|
||||
}
|
||||
ske.Mode = AEADMode(buf[0])
|
||||
}
|
||||
|
||||
if ske.Version > 5 {
|
||||
// Scalar octet count
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if ske.s2k, err = s2k.Parse(r); err != nil {
|
||||
if _, ok := err.(errors.ErrDummyPrivateKey); ok {
|
||||
return errors.UnsupportedError("missing key GNU extension in session key")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if ske.Version >= 5 {
|
||||
// AEAD IV
|
||||
iv := make([]byte, ske.Mode.IvLength())
|
||||
_, err := readFull(r, iv)
|
||||
if err != nil {
|
||||
return errors.StructuralError("cannot read AEAD IV")
|
||||
}
|
||||
|
||||
ske.iv = iv
|
||||
}
|
||||
|
||||
encryptedKey := make([]byte, maxSessionKeySizeInBytes)
|
||||
// The session key may follow. We just have to try and read to find
|
||||
// out. If it exists then we limit it to maxSessionKeySizeInBytes.
|
||||
n, err := readFull(r, encryptedKey)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if n != 0 {
|
||||
if n == maxSessionKeySizeInBytes {
|
||||
return errors.UnsupportedError("oversized encrypted session key")
|
||||
}
|
||||
ske.encryptedKey = encryptedKey[:n]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt attempts to decrypt an encrypted session key and returns the key and
|
||||
// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data
|
||||
// packet.
|
||||
func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
|
||||
key := make([]byte, ske.CipherFunc.KeySize())
|
||||
ske.s2k(key, passphrase)
|
||||
if len(ske.encryptedKey) == 0 {
|
||||
return key, ske.CipherFunc, nil
|
||||
}
|
||||
switch ske.Version {
|
||||
case 4:
|
||||
plaintextKey, cipherFunc, err := ske.decryptV4(key)
|
||||
return plaintextKey, cipherFunc, err
|
||||
case 5, 6:
|
||||
plaintextKey, err := ske.aeadDecrypt(ske.Version, key)
|
||||
return plaintextKey, CipherFunction(0), err
|
||||
}
|
||||
err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
|
||||
return nil, CipherFunction(0), err
|
||||
}
|
||||
|
||||
func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction, error) {
|
||||
// the IV is all zeros
|
||||
iv := make([]byte, ske.CipherFunc.blockSize())
|
||||
c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv)
|
||||
plaintextKey := make([]byte, len(ske.encryptedKey))
|
||||
c.XORKeyStream(plaintextKey, ske.encryptedKey)
|
||||
cipherFunc := CipherFunction(plaintextKey[0])
|
||||
if cipherFunc.blockSize() == 0 {
|
||||
return nil, ske.CipherFunc, errors.UnsupportedError(
|
||||
"unknown cipher: " + strconv.Itoa(int(cipherFunc)))
|
||||
}
|
||||
plaintextKey = plaintextKey[1:]
|
||||
if len(plaintextKey) != cipherFunc.KeySize() {
|
||||
return nil, cipherFunc, errors.StructuralError(
|
||||
"length of decrypted key not equal to cipher keysize")
|
||||
}
|
||||
return plaintextKey, cipherFunc, nil
|
||||
}
|
||||
|
||||
func (ske *SymmetricKeyEncrypted) aeadDecrypt(version int, key []byte) ([]byte, error) {
|
||||
adata := []byte{0xc3, byte(version), byte(ske.CipherFunc), byte(ske.Mode)}
|
||||
aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata, version)
|
||||
|
||||
plaintextKey, err := aead.Open(nil, ske.iv, ske.encryptedKey, adata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintextKey, nil
|
||||
}
|
||||
|
||||
// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w.
|
||||
// The packet contains a random session key, encrypted by a key derived from
|
||||
// the given passphrase. The session key is returned and must be passed to
|
||||
// SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
|
||||
cipherFunc := config.Cipher()
|
||||
|
||||
sessionKey := make([]byte, cipherFunc.KeySize())
|
||||
_, err = io.ReadFull(config.Random(), sessionKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = SerializeSymmetricKeyEncryptedReuseKey(w, sessionKey, passphrase, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
key = sessionKey
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeSymmetricKeyEncryptedReuseKey serializes a symmetric key packet to w.
|
||||
// The packet contains the given session key, encrypted by a key derived from
|
||||
// the given passphrase. The returned session key must be passed to
|
||||
// SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
// Deprecated: Use SerializeSymmetricKeyEncryptedAEADReuseKey instead.
|
||||
func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) {
|
||||
return SerializeSymmetricKeyEncryptedAEADReuseKey(w, sessionKey, passphrase, config.AEAD() != nil, config)
|
||||
}
|
||||
|
||||
// SerializeSymmetricKeyEncryptedAEADReuseKey serializes a symmetric key packet to w.
|
||||
// The packet contains the given session key, encrypted by a key derived from
|
||||
// the given passphrase. The returned session key must be passed to
|
||||
// SerializeSymmetricallyEncrypted.
|
||||
// If aeadSupported is set, SKESK v6 is used, otherwise v4.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricKeyEncryptedAEADReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, aeadSupported bool, config *Config) (err error) {
|
||||
var version int
|
||||
if aeadSupported {
|
||||
version = 6
|
||||
} else {
|
||||
version = 4
|
||||
}
|
||||
cipherFunc := config.Cipher()
|
||||
// cipherFunc must be AES
|
||||
if !cipherFunc.IsSupported() || cipherFunc < CipherAES128 || cipherFunc > CipherAES256 {
|
||||
return errors.UnsupportedError("unsupported cipher: " + strconv.Itoa(int(cipherFunc)))
|
||||
}
|
||||
|
||||
keySize := cipherFunc.KeySize()
|
||||
s2kBuf := new(bytes.Buffer)
|
||||
keyEncryptingKey := make([]byte, keySize)
|
||||
// s2k.Serialize salts and stretches the passphrase, and writes the
|
||||
// resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf.
|
||||
err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, config.S2K())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2kBytes := s2kBuf.Bytes()
|
||||
|
||||
var packetLength int
|
||||
switch version {
|
||||
case 4:
|
||||
packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
|
||||
case 5, 6:
|
||||
ivLen := config.AEAD().Mode().IvLength()
|
||||
tagLen := config.AEAD().Mode().TagLength()
|
||||
packetLength = 3 + len(s2kBytes) + ivLen + keySize + tagLen
|
||||
}
|
||||
if version > 5 {
|
||||
packetLength += 2 // additional octet count fields
|
||||
}
|
||||
|
||||
err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Symmetric Key Encrypted Version
|
||||
buf := []byte{byte(version)}
|
||||
|
||||
if version > 5 {
|
||||
// Scalar octet count
|
||||
buf = append(buf, byte(3+len(s2kBytes)+config.AEAD().Mode().IvLength()))
|
||||
}
|
||||
|
||||
// Cipher function
|
||||
buf = append(buf, byte(cipherFunc))
|
||||
|
||||
if version >= 5 {
|
||||
// AEAD mode
|
||||
buf = append(buf, byte(config.AEAD().Mode()))
|
||||
}
|
||||
if version > 5 {
|
||||
// Scalar octet count
|
||||
buf = append(buf, byte(len(s2kBytes)))
|
||||
}
|
||||
_, err = w.Write(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(s2kBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch version {
|
||||
case 4:
|
||||
iv := make([]byte, cipherFunc.blockSize())
|
||||
c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv)
|
||||
encryptedCipherAndKey := make([]byte, keySize+1)
|
||||
c.XORKeyStream(encryptedCipherAndKey, buf[1:])
|
||||
c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey)
|
||||
_, err = w.Write(encryptedCipherAndKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case 5, 6:
|
||||
mode := config.AEAD().Mode()
|
||||
adata := []byte{0xc3, byte(version), byte(cipherFunc), byte(mode)}
|
||||
aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata, version)
|
||||
|
||||
// Sample iv using random reader
|
||||
iv := make([]byte, config.AEAD().Mode().IvLength())
|
||||
_, err = io.ReadFull(config.Random(), iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Seal and write (encryptedData includes auth. tag)
|
||||
|
||||
encryptedData := aead.Seal(nil, iv, sessionKey, adata)
|
||||
_, err = w.Write(iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(encryptedData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte, version int) (aead cipher.AEAD) {
|
||||
var blockCipher cipher.Block
|
||||
if version > 5 {
|
||||
hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData)
|
||||
|
||||
encryptionKey := make([]byte, c.KeySize())
|
||||
_, _ = readFull(hkdfReader, encryptionKey)
|
||||
|
||||
blockCipher = c.new(encryptionKey)
|
||||
} else {
|
||||
blockCipher = c.new(inputKey)
|
||||
}
|
||||
return mode.new(blockCipher)
|
||||
}
|
94
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
Normal file
94
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
const aeadSaltSize = 32
|
||||
|
||||
// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The
|
||||
// encrypted Contents will consist of more OpenPGP packets. See RFC 4880,
|
||||
// sections 5.7 and 5.13.
|
||||
type SymmetricallyEncrypted struct {
|
||||
Version int
|
||||
Contents io.Reader // contains tag for version 2
|
||||
IntegrityProtected bool // If true it is type 18 (with MDC or AEAD). False is packet type 9
|
||||
|
||||
// Specific to version 1
|
||||
prefix []byte
|
||||
|
||||
// Specific to version 2
|
||||
Cipher CipherFunction
|
||||
Mode AEADMode
|
||||
ChunkSizeByte byte
|
||||
Salt [aeadSaltSize]byte
|
||||
}
|
||||
|
||||
const (
|
||||
symmetricallyEncryptedVersionMdc = 1
|
||||
symmetricallyEncryptedVersionAead = 2
|
||||
)
|
||||
|
||||
func (se *SymmetricallyEncrypted) parse(r io.Reader) error {
|
||||
if se.IntegrityProtected {
|
||||
// See RFC 4880, section 5.13.
|
||||
var buf [1]byte
|
||||
_, err := readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case symmetricallyEncryptedVersionMdc:
|
||||
se.Version = symmetricallyEncryptedVersionMdc
|
||||
case symmetricallyEncryptedVersionAead:
|
||||
se.Version = symmetricallyEncryptedVersionAead
|
||||
if err := se.parseAead(r); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
|
||||
}
|
||||
}
|
||||
se.Contents = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt returns a ReadCloser, from which the decrypted Contents of the
|
||||
// packet can be read. An incorrect key will only be detected after trying
|
||||
// to decrypt the entire data.
|
||||
func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) {
|
||||
if se.Version == symmetricallyEncryptedVersionAead {
|
||||
return se.decryptAead(key)
|
||||
}
|
||||
|
||||
return se.decryptMdc(c, key)
|
||||
}
|
||||
|
||||
// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
|
||||
// to w and returns a WriteCloser to which the to-be-encrypted packets can be
|
||||
// written.
|
||||
// If aeadSupported is set to true, SEIPDv2 is used with the indicated CipherSuite.
|
||||
// Otherwise, SEIPDv1 is used with the indicated CipherFunction.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeEncryptedKeyAEAD
|
||||
// and/or SerializeSymmetricKeyEncryptedAEADReuseKey.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, aeadSupported bool, cipherSuite CipherSuite, key []byte, config *Config) (Contents io.WriteCloser, err error) {
|
||||
writeCloser := noOpCloser{w}
|
||||
ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedIntegrityProtected)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if aeadSupported {
|
||||
return serializeSymmetricallyEncryptedAead(ciphertext, cipherSuite, config.AEADConfig.ChunkSizeByte(), config.Random(), key)
|
||||
}
|
||||
|
||||
return serializeSymmetricallyEncryptedMdc(ciphertext, c, key, config)
|
||||
}
|
168
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
generated
vendored
Normal file
168
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2023 Proton AG. 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
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// parseAead parses a V2 SEIPD packet (AEAD) as specified in
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
|
||||
func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error {
|
||||
headerData := make([]byte, 3)
|
||||
if n, err := io.ReadFull(r, headerData); n < 3 {
|
||||
return errors.StructuralError("could not read aead header: " + err.Error())
|
||||
}
|
||||
|
||||
// Cipher
|
||||
se.Cipher = CipherFunction(headerData[0])
|
||||
// cipherFunc must have block size 16 to use AEAD
|
||||
if se.Cipher.blockSize() != 16 {
|
||||
return errors.UnsupportedError("invalid aead cipher: " + strconv.Itoa(int(se.Cipher)))
|
||||
}
|
||||
|
||||
// Mode
|
||||
se.Mode = AEADMode(headerData[1])
|
||||
if se.Mode.TagLength() == 0 {
|
||||
return errors.UnsupportedError("unknown aead mode: " + strconv.Itoa(int(se.Mode)))
|
||||
}
|
||||
|
||||
// Chunk size
|
||||
se.ChunkSizeByte = headerData[2]
|
||||
if se.ChunkSizeByte > 16 {
|
||||
return errors.UnsupportedError("invalid aead chunk size byte: " + strconv.Itoa(int(se.ChunkSizeByte)))
|
||||
}
|
||||
|
||||
// Salt
|
||||
if n, err := io.ReadFull(r, se.Salt[:]); n < aeadSaltSize {
|
||||
return errors.StructuralError("could not read aead salt: " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// associatedData for chunks: tag, version, cipher, mode, chunk size byte
|
||||
func (se *SymmetricallyEncrypted) associatedData() []byte {
|
||||
return []byte{
|
||||
0xD2,
|
||||
symmetricallyEncryptedVersionAead,
|
||||
byte(se.Cipher),
|
||||
byte(se.Mode),
|
||||
se.ChunkSizeByte,
|
||||
}
|
||||
}
|
||||
|
||||
// decryptAead decrypts a V2 SEIPD packet (AEAD) as specified in
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
|
||||
func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, error) {
|
||||
if se.Cipher.KeySize() != len(inputKey) {
|
||||
return nil, errors.StructuralError(fmt.Sprintf("invalid session key length for cipher: got %d bytes, but expected %d bytes", len(inputKey), se.Cipher.KeySize()))
|
||||
}
|
||||
|
||||
aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
|
||||
// Carry the first tagLen bytes
|
||||
chunkSize := decodeAEADChunkSize(se.ChunkSizeByte)
|
||||
tagLen := se.Mode.TagLength()
|
||||
chunkBytes := make([]byte, chunkSize+tagLen*2)
|
||||
peekedBytes := chunkBytes[chunkSize+tagLen:]
|
||||
n, err := io.ReadFull(se.Contents, peekedBytes)
|
||||
if n < tagLen || (err != nil && err != io.EOF) {
|
||||
return nil, errors.StructuralError("not enough data to decrypt:" + err.Error())
|
||||
}
|
||||
|
||||
return &aeadDecrypter{
|
||||
aeadCrypter: aeadCrypter{
|
||||
aead: aead,
|
||||
chunkSize: decodeAEADChunkSize(se.ChunkSizeByte),
|
||||
nonce: nonce,
|
||||
associatedData: se.associatedData(),
|
||||
chunkIndex: nonce[len(nonce)-8:],
|
||||
packetTag: packetTypeSymmetricallyEncryptedIntegrityProtected,
|
||||
},
|
||||
reader: se.Contents,
|
||||
chunkBytes: chunkBytes,
|
||||
peekedBytes: peekedBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// serializeSymmetricallyEncryptedAead encrypts to a writer a V2 SEIPD packet (AEAD) as specified in
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
|
||||
func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite CipherSuite, chunkSizeByte byte, rand io.Reader, inputKey []byte) (Contents io.WriteCloser, err error) {
|
||||
// cipherFunc must have block size 16 to use AEAD
|
||||
if cipherSuite.Cipher.blockSize() != 16 {
|
||||
return nil, errors.InvalidArgumentError("invalid aead cipher function")
|
||||
}
|
||||
|
||||
if cipherSuite.Cipher.KeySize() != len(inputKey) {
|
||||
return nil, errors.InvalidArgumentError("error in aead serialization: bad key length")
|
||||
}
|
||||
|
||||
// Data for en/decryption: tag, version, cipher, aead mode, chunk size
|
||||
prefix := []byte{
|
||||
0xD2,
|
||||
symmetricallyEncryptedVersionAead,
|
||||
byte(cipherSuite.Cipher),
|
||||
byte(cipherSuite.Mode),
|
||||
chunkSizeByte,
|
||||
}
|
||||
|
||||
// Write header (that correspond to prefix except first byte)
|
||||
n, err := ciphertext.Write(prefix[1:])
|
||||
if err != nil || n < 4 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Random salt
|
||||
salt := make([]byte, aeadSaltSize)
|
||||
if _, err := io.ReadFull(rand, salt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := ciphertext.Write(salt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aead, nonce := getSymmetricallyEncryptedAeadInstance(cipherSuite.Cipher, cipherSuite.Mode, inputKey, salt, prefix)
|
||||
|
||||
chunkSize := decodeAEADChunkSize(chunkSizeByte)
|
||||
tagLen := aead.Overhead()
|
||||
chunkBytes := make([]byte, chunkSize+tagLen)
|
||||
return &aeadEncrypter{
|
||||
aeadCrypter: aeadCrypter{
|
||||
aead: aead,
|
||||
chunkSize: chunkSize,
|
||||
associatedData: prefix,
|
||||
nonce: nonce,
|
||||
chunkIndex: nonce[len(nonce)-8:],
|
||||
packetTag: packetTypeSymmetricallyEncryptedIntegrityProtected,
|
||||
},
|
||||
writer: ciphertext,
|
||||
chunkBytes: chunkBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getSymmetricallyEncryptedAeadInstance(c CipherFunction, mode AEADMode, inputKey, salt, associatedData []byte) (aead cipher.AEAD, nonce []byte) {
|
||||
hkdfReader := hkdf.New(sha256.New, inputKey, salt, associatedData)
|
||||
|
||||
encryptionKey := make([]byte, c.KeySize())
|
||||
_, _ = readFull(hkdfReader, encryptionKey)
|
||||
|
||||
nonce = make([]byte, mode.IvLength())
|
||||
|
||||
// Last 64 bits of nonce are the counter
|
||||
_, _ = readFull(hkdfReader, nonce[:len(nonce)-8])
|
||||
|
||||
blockCipher := c.new(encryptionKey)
|
||||
aead = mode.new(blockCipher)
|
||||
|
||||
return
|
||||
}
|
256
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
generated
vendored
Normal file
256
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
generated
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/sha1"
|
||||
"crypto/subtle"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// seMdcReader wraps an io.Reader with a no-op Close method.
|
||||
type seMdcReader struct {
|
||||
in io.Reader
|
||||
}
|
||||
|
||||
func (ser seMdcReader) Read(buf []byte) (int, error) {
|
||||
return ser.in.Read(buf)
|
||||
}
|
||||
|
||||
func (ser seMdcReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (se *SymmetricallyEncrypted) decryptMdc(c CipherFunction, key []byte) (io.ReadCloser, error) {
|
||||
if !c.IsSupported() {
|
||||
return nil, errors.UnsupportedError("unsupported cipher: " + strconv.Itoa(int(c)))
|
||||
}
|
||||
|
||||
if len(key) != c.KeySize() {
|
||||
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
|
||||
}
|
||||
|
||||
if se.prefix == nil {
|
||||
se.prefix = make([]byte, c.blockSize()+2)
|
||||
_, err := readFull(se.Contents, se.prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if len(se.prefix) != c.blockSize()+2 {
|
||||
return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths")
|
||||
}
|
||||
|
||||
ocfbResync := OCFBResync
|
||||
if se.IntegrityProtected {
|
||||
// MDC packets use a different form of OCFB mode.
|
||||
ocfbResync = OCFBNoResync
|
||||
}
|
||||
|
||||
s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync)
|
||||
|
||||
plaintext := cipher.StreamReader{S: s, R: se.Contents}
|
||||
|
||||
if se.IntegrityProtected {
|
||||
// IntegrityProtected packets have an embedded hash that we need to check.
|
||||
h := sha1.New()
|
||||
h.Write(se.prefix)
|
||||
return &seMDCReader{in: plaintext, h: h}, nil
|
||||
}
|
||||
|
||||
// Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser.
|
||||
return seMdcReader{plaintext}, nil
|
||||
}
|
||||
|
||||
const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size
|
||||
|
||||
// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold
|
||||
// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an
|
||||
// MDC packet containing a hash of the previous Contents which is checked
|
||||
// against the running hash. See RFC 4880, section 5.13.
|
||||
type seMDCReader struct {
|
||||
in io.Reader
|
||||
h hash.Hash
|
||||
trailer [mdcTrailerSize]byte
|
||||
scratch [mdcTrailerSize]byte
|
||||
trailerUsed int
|
||||
error bool
|
||||
eof bool
|
||||
}
|
||||
|
||||
func (ser *seMDCReader) Read(buf []byte) (n int, err error) {
|
||||
if ser.error {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
if ser.eof {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
|
||||
// If we haven't yet filled the trailer buffer then we must do that
|
||||
// first.
|
||||
for ser.trailerUsed < mdcTrailerSize {
|
||||
n, err = ser.in.Read(ser.trailer[ser.trailerUsed:])
|
||||
ser.trailerUsed += n
|
||||
if err == io.EOF {
|
||||
if ser.trailerUsed != mdcTrailerSize {
|
||||
n = 0
|
||||
err = io.ErrUnexpectedEOF
|
||||
ser.error = true
|
||||
return
|
||||
}
|
||||
ser.eof = true
|
||||
n = 0
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n = 0
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a short read then we read into a temporary buffer and shift
|
||||
// the data into the caller's buffer.
|
||||
if len(buf) <= mdcTrailerSize {
|
||||
n, err = readFull(ser.in, ser.scratch[:len(buf)])
|
||||
copy(buf, ser.trailer[:n])
|
||||
ser.h.Write(buf[:n])
|
||||
copy(ser.trailer[:], ser.trailer[n:])
|
||||
copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:])
|
||||
if n < len(buf) {
|
||||
ser.eof = true
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n, err = ser.in.Read(buf[mdcTrailerSize:])
|
||||
copy(buf, ser.trailer[:])
|
||||
ser.h.Write(buf[:n])
|
||||
copy(ser.trailer[:], buf[n:])
|
||||
|
||||
if err == io.EOF {
|
||||
ser.eof = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// This is a new-format packet tag byte for a type 19 (Integrity Protected) packet.
|
||||
const mdcPacketTagByte = byte(0x80) | 0x40 | 19
|
||||
|
||||
func (ser *seMDCReader) Close() error {
|
||||
if ser.error {
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
|
||||
for !ser.eof {
|
||||
// We haven't seen EOF so we need to read to the end
|
||||
var buf [1024]byte
|
||||
_, err := ser.Read(buf[:])
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
}
|
||||
|
||||
ser.h.Write(ser.trailer[:2])
|
||||
|
||||
final := ser.h.Sum(nil)
|
||||
if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
// The hash already includes the MDC header, but we still check its value
|
||||
// to confirm encryption correctness
|
||||
if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// An seMDCWriter writes through to an io.WriteCloser while maintains a running
|
||||
// hash of the data written. On close, it emits an MDC packet containing the
|
||||
// running hash.
|
||||
type seMDCWriter struct {
|
||||
w io.WriteCloser
|
||||
h hash.Hash
|
||||
}
|
||||
|
||||
func (w *seMDCWriter) Write(buf []byte) (n int, err error) {
|
||||
w.h.Write(buf)
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
|
||||
func (w *seMDCWriter) Close() (err error) {
|
||||
var buf [mdcTrailerSize]byte
|
||||
|
||||
buf[0] = mdcPacketTagByte
|
||||
buf[1] = sha1.Size
|
||||
w.h.Write(buf[:2])
|
||||
digest := w.h.Sum(nil)
|
||||
copy(buf[2:], digest)
|
||||
|
||||
_, err = w.w.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return w.w.Close()
|
||||
}
|
||||
|
||||
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
|
||||
type noOpCloser struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (c noOpCloser) Write(data []byte) (n int, err error) {
|
||||
return c.w.Write(data)
|
||||
}
|
||||
|
||||
func (c noOpCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func serializeSymmetricallyEncryptedMdc(ciphertext io.WriteCloser, c CipherFunction, key []byte, config *Config) (Contents io.WriteCloser, err error) {
|
||||
// Disallow old cipher suites
|
||||
if !c.IsSupported() || c < CipherAES128 {
|
||||
return nil, errors.InvalidArgumentError("invalid mdc cipher function")
|
||||
}
|
||||
|
||||
if c.KeySize() != len(key) {
|
||||
return nil, errors.InvalidArgumentError("error in mdc serialization: bad key length")
|
||||
}
|
||||
|
||||
_, err = ciphertext.Write([]byte{symmetricallyEncryptedVersionMdc})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
block := c.new(key)
|
||||
blockSize := block.BlockSize()
|
||||
iv := make([]byte, blockSize)
|
||||
_, err = io.ReadFull(config.Random(), iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
|
||||
_, err = ciphertext.Write(prefix)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
plaintext := cipher.StreamWriter{S: s, W: ciphertext}
|
||||
|
||||
h := sha1.New()
|
||||
h.Write(iv)
|
||||
h.Write(iv[blockSize-2:])
|
||||
Contents = &seMDCWriter{w: plaintext, h: h}
|
||||
return
|
||||
}
|
100
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
generated
vendored
Normal file
100
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2013 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
)
|
||||
|
||||
const UserAttrImageSubpacket = 1
|
||||
|
||||
// UserAttribute is capable of storing other types of data about a user
|
||||
// beyond name, email and a text comment. In practice, user attributes are typically used
|
||||
// to store a signed thumbnail photo JPEG image of the user.
|
||||
// See RFC 4880, section 5.12.
|
||||
type UserAttribute struct {
|
||||
Contents []*OpaqueSubpacket
|
||||
}
|
||||
|
||||
// NewUserAttributePhoto creates a user attribute packet
|
||||
// containing the given images.
|
||||
func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) {
|
||||
uat = new(UserAttribute)
|
||||
for _, photo := range photos {
|
||||
var buf bytes.Buffer
|
||||
// RFC 4880, Section 5.12.1.
|
||||
data := []byte{
|
||||
0x10, 0x00, // Little-endian image header length (16 bytes)
|
||||
0x01, // Image header version 1
|
||||
0x01, // JPEG
|
||||
0, 0, 0, 0, // 12 reserved octets, must be all zero.
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0}
|
||||
if _, err = buf.Write(data); err != nil {
|
||||
return
|
||||
}
|
||||
if err = jpeg.Encode(&buf, photo, nil); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lengthBuf := make([]byte, 5)
|
||||
n := serializeSubpacketLength(lengthBuf, len(buf.Bytes())+1)
|
||||
lengthBuf = lengthBuf[:n]
|
||||
|
||||
uat.Contents = append(uat.Contents, &OpaqueSubpacket{
|
||||
SubType: UserAttrImageSubpacket,
|
||||
EncodedLength: lengthBuf,
|
||||
Contents: buf.Bytes(),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewUserAttribute creates a new user attribute packet containing the given subpackets.
|
||||
func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute {
|
||||
return &UserAttribute{Contents: contents}
|
||||
}
|
||||
|
||||
func (uat *UserAttribute) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.13
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uat.Contents, err = OpaqueSubpackets(b)
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the user attribute to w in the form of an OpenPGP packet, including
|
||||
// header.
|
||||
func (uat *UserAttribute) Serialize(w io.Writer) (err error) {
|
||||
var buf bytes.Buffer
|
||||
for _, sp := range uat.Contents {
|
||||
err = sp.Serialize(&buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// ImageData returns zero or more byte slices, each containing
|
||||
// JPEG File Interchange Format (JFIF), for each photo in the
|
||||
// user attribute packet.
|
||||
func (uat *UserAttribute) ImageData() (imageData [][]byte) {
|
||||
for _, sp := range uat.Contents {
|
||||
if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 {
|
||||
imageData = append(imageData, sp.Contents[16:])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
166
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
generated
vendored
Normal file
166
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
generated
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserId contains text that is intended to represent the name and email
|
||||
// address of the key holder. See RFC 4880, section 5.11. By convention, this
|
||||
// takes the form "Full Name (Comment) <email@example.com>"
|
||||
type UserId struct {
|
||||
Id string // By convention, this takes the form "Full Name (Comment) <email@example.com>" which is split out in the fields below.
|
||||
|
||||
Name, Comment, Email string
|
||||
}
|
||||
|
||||
func hasInvalidCharacters(s string) bool {
|
||||
for _, c := range s {
|
||||
switch c {
|
||||
case '(', ')', '<', '>', 0:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewUserId returns a UserId or nil if any of the arguments contain invalid
|
||||
// characters. The invalid characters are '\x00', '(', ')', '<' and '>'
|
||||
func NewUserId(name, comment, email string) *UserId {
|
||||
// RFC 4880 doesn't deal with the structure of userid strings; the
|
||||
// name, comment and email form is just a convention. However, there's
|
||||
// no convention about escaping the metacharacters and GPG just refuses
|
||||
// to create user ids where, say, the name contains a '('. We mirror
|
||||
// this behaviour.
|
||||
|
||||
if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) {
|
||||
return nil
|
||||
}
|
||||
|
||||
uid := new(UserId)
|
||||
uid.Name, uid.Comment, uid.Email = name, comment, email
|
||||
uid.Id = name
|
||||
if len(comment) > 0 {
|
||||
if len(uid.Id) > 0 {
|
||||
uid.Id += " "
|
||||
}
|
||||
uid.Id += "("
|
||||
uid.Id += comment
|
||||
uid.Id += ")"
|
||||
}
|
||||
if len(email) > 0 {
|
||||
if len(uid.Id) > 0 {
|
||||
uid.Id += " "
|
||||
}
|
||||
uid.Id += "<"
|
||||
uid.Id += email
|
||||
uid.Id += ">"
|
||||
}
|
||||
return uid
|
||||
}
|
||||
|
||||
func (uid *UserId) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.11
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uid.Id = string(b)
|
||||
uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id)
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals uid to w in the form of an OpenPGP packet, including
|
||||
// header.
|
||||
func (uid *UserId) Serialize(w io.Writer) error {
|
||||
err := serializeHeader(w, packetTypeUserId, len(uid.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(uid.Id))
|
||||
return err
|
||||
}
|
||||
|
||||
// parseUserId extracts the name, comment and email from a user id string that
|
||||
// is formatted as "Full Name (Comment) <email@example.com>".
|
||||
func parseUserId(id string) (name, comment, email string) {
|
||||
var n, c, e struct {
|
||||
start, end int
|
||||
}
|
||||
var state int
|
||||
|
||||
for offset, rune := range id {
|
||||
switch state {
|
||||
case 0:
|
||||
// Entering name
|
||||
n.start = offset
|
||||
state = 1
|
||||
fallthrough
|
||||
case 1:
|
||||
// In name
|
||||
if rune == '(' {
|
||||
state = 2
|
||||
n.end = offset
|
||||
} else if rune == '<' {
|
||||
state = 5
|
||||
n.end = offset
|
||||
}
|
||||
case 2:
|
||||
// Entering comment
|
||||
c.start = offset
|
||||
state = 3
|
||||
fallthrough
|
||||
case 3:
|
||||
// In comment
|
||||
if rune == ')' {
|
||||
state = 4
|
||||
c.end = offset
|
||||
}
|
||||
case 4:
|
||||
// Between comment and email
|
||||
if rune == '<' {
|
||||
state = 5
|
||||
}
|
||||
case 5:
|
||||
// Entering email
|
||||
e.start = offset
|
||||
state = 6
|
||||
fallthrough
|
||||
case 6:
|
||||
// In email
|
||||
if rune == '>' {
|
||||
state = 7
|
||||
e.end = offset
|
||||
}
|
||||
default:
|
||||
// After email
|
||||
}
|
||||
}
|
||||
switch state {
|
||||
case 1:
|
||||
// ended in the name
|
||||
n.end = len(id)
|
||||
case 3:
|
||||
// ended in comment
|
||||
c.end = len(id)
|
||||
case 6:
|
||||
// ended in email
|
||||
e.end = len(id)
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(id[n.start:n.end])
|
||||
comment = strings.TrimSpace(id[c.start:c.end])
|
||||
email = strings.TrimSpace(id[e.start:e.end])
|
||||
|
||||
// RFC 2822 3.4: alternate simple form of a mailbox
|
||||
if email == "" && strings.ContainsRune(name, '@') {
|
||||
email = name
|
||||
name = ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
619
vendor/github.com/ProtonMail/go-crypto/openpgp/read.go
generated
vendored
Normal file
619
vendor/github.com/ProtonMail/go-crypto/openpgp/read.go
generated
vendored
Normal file
@ -0,0 +1,619 @@
|
||||
// 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 implements high level operations on OpenPGP messages.
|
||||
package openpgp // import "github.com/ProtonMail/go-crypto/openpgp"
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"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/sha3"
|
||||
)
|
||||
|
||||
// SignatureType is the armor type for a PGP signature.
|
||||
var SignatureType = "PGP SIGNATURE"
|
||||
|
||||
// readArmored reads an armored block with the given type.
|
||||
func readArmored(r io.Reader, expectedType string) (body io.Reader, err error) {
|
||||
block, err := armor.Decode(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if block.Type != expectedType {
|
||||
return nil, errors.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type)
|
||||
}
|
||||
|
||||
return block.Body, nil
|
||||
}
|
||||
|
||||
// MessageDetails contains the result of parsing an OpenPGP encrypted and/or
|
||||
// signed message.
|
||||
type MessageDetails struct {
|
||||
IsEncrypted bool // true if the message was encrypted.
|
||||
EncryptedToKeyIds []uint64 // the list of recipient key ids.
|
||||
IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message.
|
||||
DecryptedWith Key // the private key used to decrypt the message, if any.
|
||||
IsSigned bool // true if the message is signed.
|
||||
SignedByKeyId uint64 // the key id of the signer, if any.
|
||||
SignedByFingerprint []byte // the key fingerprint of the signer, if any.
|
||||
SignedBy *Key // the key of the signer, if available.
|
||||
LiteralData *packet.LiteralData // the metadata of the contents
|
||||
UnverifiedBody io.Reader // the contents of the message.
|
||||
|
||||
// If IsSigned is true and SignedBy is non-zero then the signature will
|
||||
// be verified as UnverifiedBody is read. The signature cannot be
|
||||
// checked until the whole of UnverifiedBody is read so UnverifiedBody
|
||||
// must be consumed until EOF before the data can be trusted. Even if a
|
||||
// message isn't signed (or the signer is unknown) the data may contain
|
||||
// an authentication code that is only checked once UnverifiedBody has
|
||||
// been consumed. Once EOF has been seen, the following fields are
|
||||
// valid. (An authentication code failure is reported as a
|
||||
// SignatureError error when reading from UnverifiedBody.)
|
||||
Signature *packet.Signature // the signature packet itself.
|
||||
SignatureError error // nil if the signature is good.
|
||||
UnverifiedSignatures []*packet.Signature // all other unverified signature packets.
|
||||
|
||||
decrypted io.ReadCloser
|
||||
}
|
||||
|
||||
// A PromptFunction is used as a callback by functions that may need to decrypt
|
||||
// a private key, or prompt for a passphrase. It is called with a list of
|
||||
// acceptable, encrypted private keys and a boolean that indicates whether a
|
||||
// passphrase is usable. It should either decrypt a private key or return a
|
||||
// passphrase to try. If the decrypted private key or given passphrase isn't
|
||||
// correct, the function will be called again, forever. Any error returned will
|
||||
// be passed up.
|
||||
type PromptFunction func(keys []Key, symmetric bool) ([]byte, error)
|
||||
|
||||
// A keyEnvelopePair is used to store a private key with the envelope that
|
||||
// contains a symmetric key, encrypted with that key.
|
||||
type keyEnvelopePair struct {
|
||||
key Key
|
||||
encryptedKey *packet.EncryptedKey
|
||||
}
|
||||
|
||||
// ReadMessage parses an OpenPGP message that may be signed and/or encrypted.
|
||||
// The given KeyRing should contain both public keys (for signature
|
||||
// verification) and, possibly encrypted, private keys for decrypting.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) {
|
||||
var p packet.Packet
|
||||
|
||||
var symKeys []*packet.SymmetricKeyEncrypted
|
||||
var pubKeys []keyEnvelopePair
|
||||
// Integrity protected encrypted packet: SymmetricallyEncrypted or AEADEncrypted
|
||||
var edp packet.EncryptedDataPacket
|
||||
|
||||
packets := packet.NewReader(r)
|
||||
md = new(MessageDetails)
|
||||
md.IsEncrypted = true
|
||||
|
||||
// The message, if encrypted, starts with a number of packets
|
||||
// containing an encrypted decryption key. The decryption key is either
|
||||
// encrypted to a public key, or with a passphrase. This loop
|
||||
// collects these packets.
|
||||
ParsePackets:
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p := p.(type) {
|
||||
case *packet.SymmetricKeyEncrypted:
|
||||
// This packet contains the decryption key encrypted with a passphrase.
|
||||
md.IsSymmetricallyEncrypted = true
|
||||
symKeys = append(symKeys, p)
|
||||
case *packet.EncryptedKey:
|
||||
// This packet contains the decryption key encrypted to a public key.
|
||||
md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId)
|
||||
switch p.Algo {
|
||||
case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH, packet.PubKeyAlgoX25519, packet.PubKeyAlgoX448:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if keyring != nil {
|
||||
var keys []Key
|
||||
if p.KeyId == 0 {
|
||||
keys = keyring.DecryptionKeys()
|
||||
} else {
|
||||
keys = keyring.KeysById(p.KeyId)
|
||||
}
|
||||
for _, k := range keys {
|
||||
pubKeys = append(pubKeys, keyEnvelopePair{k, p})
|
||||
}
|
||||
}
|
||||
case *packet.SymmetricallyEncrypted:
|
||||
if !p.IntegrityProtected && !config.AllowUnauthenticatedMessages() {
|
||||
return nil, errors.UnsupportedError("message is not integrity protected")
|
||||
}
|
||||
edp = p
|
||||
break ParsePackets
|
||||
case *packet.AEADEncrypted:
|
||||
edp = p
|
||||
break ParsePackets
|
||||
case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature:
|
||||
// This message isn't encrypted.
|
||||
if len(symKeys) != 0 || len(pubKeys) != 0 {
|
||||
return nil, errors.StructuralError("key material not followed by encrypted message")
|
||||
}
|
||||
packets.Unread(p)
|
||||
return readSignedMessage(packets, nil, keyring, config)
|
||||
}
|
||||
}
|
||||
|
||||
var candidates []Key
|
||||
var decrypted io.ReadCloser
|
||||
|
||||
// Now that we have the list of encrypted keys we need to decrypt at
|
||||
// least one of them or, if we cannot, we need to call the prompt
|
||||
// function so that it can decrypt a key or give us a passphrase.
|
||||
FindKey:
|
||||
for {
|
||||
// See if any of the keys already have a private key available
|
||||
candidates = candidates[:0]
|
||||
candidateFingerprints := make(map[string]bool)
|
||||
|
||||
for _, pk := range pubKeys {
|
||||
if pk.key.PrivateKey == nil {
|
||||
continue
|
||||
}
|
||||
if !pk.key.PrivateKey.Encrypted {
|
||||
if len(pk.encryptedKey.Key) == 0 {
|
||||
errDec := pk.encryptedKey.Decrypt(pk.key.PrivateKey, config)
|
||||
if errDec != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Try to decrypt symmetrically encrypted
|
||||
decrypted, err = edp.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key)
|
||||
if err != nil && err != errors.ErrKeyIncorrect {
|
||||
return nil, err
|
||||
}
|
||||
if decrypted != nil {
|
||||
md.DecryptedWith = pk.key
|
||||
break FindKey
|
||||
}
|
||||
} else {
|
||||
fpr := string(pk.key.PublicKey.Fingerprint[:])
|
||||
if v := candidateFingerprints[fpr]; v {
|
||||
continue
|
||||
}
|
||||
candidates = append(candidates, pk.key)
|
||||
candidateFingerprints[fpr] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(candidates) == 0 && len(symKeys) == 0 {
|
||||
return nil, errors.ErrKeyIncorrect
|
||||
}
|
||||
|
||||
if prompt == nil {
|
||||
return nil, errors.ErrKeyIncorrect
|
||||
}
|
||||
|
||||
passphrase, err := prompt(candidates, len(symKeys) != 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Try the symmetric passphrase first
|
||||
if len(symKeys) != 0 && passphrase != nil {
|
||||
for _, s := range symKeys {
|
||||
key, cipherFunc, err := s.Decrypt(passphrase)
|
||||
// In v4, on wrong passphrase, session key decryption is very likely to result in an invalid cipherFunc:
|
||||
// only for < 5% of cases we will proceed to decrypt the data
|
||||
if err == nil {
|
||||
decrypted, err = edp.Decrypt(cipherFunc, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if decrypted != nil {
|
||||
break FindKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
md.decrypted = decrypted
|
||||
if err := packets.Push(decrypted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mdFinal, sensitiveParsingErr := readSignedMessage(packets, md, keyring, config)
|
||||
if sensitiveParsingErr != nil {
|
||||
return nil, errors.HandleSensitiveParsingError(sensitiveParsingErr, md.decrypted != nil)
|
||||
}
|
||||
return mdFinal, nil
|
||||
}
|
||||
|
||||
// readSignedMessage reads a possibly signed message if mdin is non-zero then
|
||||
// that structure is updated and returned. Otherwise a fresh MessageDetails is
|
||||
// used.
|
||||
func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing, config *packet.Config) (md *MessageDetails, err error) {
|
||||
if mdin == nil {
|
||||
mdin = new(MessageDetails)
|
||||
}
|
||||
md = mdin
|
||||
|
||||
var p packet.Packet
|
||||
var h hash.Hash
|
||||
var wrappedHash hash.Hash
|
||||
var prevLast bool
|
||||
FindLiteralData:
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p := p.(type) {
|
||||
case *packet.Compressed:
|
||||
if err := packets.Push(p.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *packet.OnePassSignature:
|
||||
if prevLast {
|
||||
return nil, errors.UnsupportedError("nested signature packets")
|
||||
}
|
||||
|
||||
if p.IsLast {
|
||||
prevLast = true
|
||||
}
|
||||
|
||||
h, wrappedHash, err = hashForSignature(p.Hash, p.SigType, p.Salt)
|
||||
if err != nil {
|
||||
md.SignatureError = err
|
||||
}
|
||||
|
||||
md.IsSigned = true
|
||||
if p.Version == 6 {
|
||||
md.SignedByFingerprint = p.KeyFingerprint
|
||||
}
|
||||
md.SignedByKeyId = p.KeyId
|
||||
|
||||
if keyring != nil {
|
||||
keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign)
|
||||
if len(keys) > 0 {
|
||||
md.SignedBy = &keys[0]
|
||||
}
|
||||
}
|
||||
case *packet.LiteralData:
|
||||
md.LiteralData = p
|
||||
break FindLiteralData
|
||||
}
|
||||
}
|
||||
|
||||
if md.IsSigned && md.SignatureError == nil {
|
||||
md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md, config}
|
||||
} else if md.decrypted != nil {
|
||||
md.UnverifiedBody = &checkReader{md, false}
|
||||
} else {
|
||||
md.UnverifiedBody = md.LiteralData.Body
|
||||
}
|
||||
|
||||
return md, nil
|
||||
}
|
||||
|
||||
func wrapHashForSignature(hashFunc hash.Hash, sigType packet.SignatureType) (hash.Hash, error) {
|
||||
switch sigType {
|
||||
case packet.SigTypeBinary:
|
||||
return hashFunc, nil
|
||||
case packet.SigTypeText:
|
||||
return NewCanonicalTextHash(hashFunc), nil
|
||||
}
|
||||
return nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType)))
|
||||
}
|
||||
|
||||
// hashForSignature returns a pair of hashes that can be used to verify a
|
||||
// signature. The signature may specify that the contents of the signed message
|
||||
// should be preprocessed (i.e. to normalize line endings). Thus this function
|
||||
// returns two hashes. The second should be used to hash the message itself and
|
||||
// performs any needed preprocessing.
|
||||
func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType, sigSalt []byte) (hash.Hash, hash.Hash, error) {
|
||||
if _, ok := algorithm.HashToHashIdWithSha1(hashFunc); !ok {
|
||||
return nil, nil, errors.UnsupportedError("unsupported hash function")
|
||||
}
|
||||
if !hashFunc.Available() {
|
||||
return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashFunc)))
|
||||
}
|
||||
h := hashFunc.New()
|
||||
if sigSalt != nil {
|
||||
h.Write(sigSalt)
|
||||
}
|
||||
wrappedHash, err := wrapHashForSignature(h, sigType)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
switch sigType {
|
||||
case packet.SigTypeBinary:
|
||||
return h, wrappedHash, nil
|
||||
case packet.SigTypeText:
|
||||
return h, wrappedHash, nil
|
||||
}
|
||||
return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType)))
|
||||
}
|
||||
|
||||
// checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF
|
||||
// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger
|
||||
// MDC checks.
|
||||
type checkReader struct {
|
||||
md *MessageDetails
|
||||
checked bool
|
||||
}
|
||||
|
||||
func (cr *checkReader) Read(buf []byte) (int, error) {
|
||||
n, sensitiveParsingError := cr.md.LiteralData.Body.Read(buf)
|
||||
if sensitiveParsingError == io.EOF {
|
||||
if cr.checked {
|
||||
// Only check once
|
||||
return n, io.EOF
|
||||
}
|
||||
mdcErr := cr.md.decrypted.Close()
|
||||
if mdcErr != nil {
|
||||
return n, mdcErr
|
||||
}
|
||||
cr.checked = true
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
if sensitiveParsingError != nil {
|
||||
return n, errors.HandleSensitiveParsingError(sensitiveParsingError, true)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes
|
||||
// the data as it is read. When it sees an EOF from the underlying io.Reader
|
||||
// it parses and checks a trailing Signature packet and triggers any MDC checks.
|
||||
type signatureCheckReader struct {
|
||||
packets *packet.Reader
|
||||
h, wrappedHash hash.Hash
|
||||
md *MessageDetails
|
||||
config *packet.Config
|
||||
}
|
||||
|
||||
func (scr *signatureCheckReader) Read(buf []byte) (int, error) {
|
||||
n, sensitiveParsingError := scr.md.LiteralData.Body.Read(buf)
|
||||
|
||||
// Hash only if required
|
||||
if scr.md.SignedBy != nil {
|
||||
scr.wrappedHash.Write(buf[:n])
|
||||
}
|
||||
|
||||
readsDecryptedData := scr.md.decrypted != nil
|
||||
if sensitiveParsingError == io.EOF {
|
||||
var p packet.Packet
|
||||
var readError error
|
||||
var sig *packet.Signature
|
||||
|
||||
p, readError = scr.packets.Next()
|
||||
for readError == nil {
|
||||
var ok bool
|
||||
if sig, ok = p.(*packet.Signature); ok {
|
||||
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
|
||||
sig.Metadata = scr.md.LiteralData
|
||||
}
|
||||
|
||||
// If signature KeyID matches
|
||||
if scr.md.SignedBy != nil && *sig.IssuerKeyId == scr.md.SignedByKeyId {
|
||||
key := scr.md.SignedBy
|
||||
signatureError := key.PublicKey.VerifySignature(scr.h, sig)
|
||||
if signatureError == nil {
|
||||
signatureError = checkMessageSignatureDetails(key, sig, scr.config)
|
||||
}
|
||||
scr.md.Signature = sig
|
||||
scr.md.SignatureError = signatureError
|
||||
} else {
|
||||
scr.md.UnverifiedSignatures = append(scr.md.UnverifiedSignatures, sig)
|
||||
}
|
||||
}
|
||||
|
||||
p, readError = scr.packets.Next()
|
||||
}
|
||||
|
||||
if scr.md.SignedBy != nil && scr.md.Signature == nil {
|
||||
if scr.md.UnverifiedSignatures == nil {
|
||||
scr.md.SignatureError = errors.StructuralError("LiteralData not followed by signature")
|
||||
} else {
|
||||
scr.md.SignatureError = errors.StructuralError("No matching signature found")
|
||||
}
|
||||
}
|
||||
|
||||
// The SymmetricallyEncrypted packet, if any, might have an
|
||||
// unsigned hash of its own. In order to check this we need to
|
||||
// close that Reader.
|
||||
if scr.md.decrypted != nil {
|
||||
if sensitiveParsingError := scr.md.decrypted.Close(); sensitiveParsingError != nil {
|
||||
return n, errors.HandleSensitiveParsingError(sensitiveParsingError, true)
|
||||
}
|
||||
}
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
if sensitiveParsingError != nil {
|
||||
return n, errors.HandleSensitiveParsingError(sensitiveParsingError, readsDecryptedData)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// VerifyDetachedSignature takes a signed file and a detached signature and
|
||||
// returns the signature packet and the entity the signature was signed by,
|
||||
// if any, and a possible signature verification error.
|
||||
// If the signer isn't known, ErrUnknownIssuer is returned.
|
||||
func VerifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
|
||||
return verifyDetachedSignature(keyring, signed, signature, nil, false, config)
|
||||
}
|
||||
|
||||
// VerifyDetachedSignatureAndHash performs the same actions as
|
||||
// VerifyDetachedSignature and checks that the expected hash functions were used.
|
||||
func VerifyDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
|
||||
return verifyDetachedSignature(keyring, signed, signature, expectedHashes, true, config)
|
||||
}
|
||||
|
||||
// CheckDetachedSignature takes a signed file and a detached signature and
|
||||
// returns the entity the signature was signed by, if any, and a possible
|
||||
// signature verification error. If the signer isn't known,
|
||||
// ErrUnknownIssuer is returned.
|
||||
func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) {
|
||||
_, signer, err = verifyDetachedSignature(keyring, signed, signature, nil, false, config)
|
||||
return
|
||||
}
|
||||
|
||||
// CheckDetachedSignatureAndHash performs the same actions as
|
||||
// CheckDetachedSignature and checks that the expected hash functions were used.
|
||||
func CheckDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (signer *Entity, err error) {
|
||||
_, signer, err = verifyDetachedSignature(keyring, signed, signature, expectedHashes, true, config)
|
||||
return
|
||||
}
|
||||
|
||||
func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, checkHashes bool, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
|
||||
var issuerKeyId uint64
|
||||
var hashFunc crypto.Hash
|
||||
var sigType packet.SignatureType
|
||||
var keys []Key
|
||||
var p packet.Packet
|
||||
|
||||
packets := packet.NewReader(signature)
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return nil, nil, errors.ErrUnknownIssuer
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
sig, ok = p.(*packet.Signature)
|
||||
if !ok {
|
||||
return nil, nil, errors.StructuralError("non signature packet found")
|
||||
}
|
||||
if sig.IssuerKeyId == nil {
|
||||
return nil, nil, errors.StructuralError("signature doesn't have an issuer")
|
||||
}
|
||||
issuerKeyId = *sig.IssuerKeyId
|
||||
hashFunc = sig.Hash
|
||||
sigType = sig.SigType
|
||||
if checkHashes {
|
||||
matchFound := false
|
||||
// check for hashes
|
||||
for _, expectedHash := range expectedHashes {
|
||||
if hashFunc == expectedHash {
|
||||
matchFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matchFound {
|
||||
return nil, nil, errors.StructuralError("hash algorithm or salt mismatch with cleartext message headers")
|
||||
}
|
||||
}
|
||||
keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign)
|
||||
if len(keys) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
h, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
wrappedHash, err := wrapHashForSignature(h, sigType)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
err = key.PublicKey.VerifySignature(h, sig)
|
||||
if err == nil {
|
||||
return sig, key.Entity, checkMessageSignatureDetails(&key, sig, config)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// CheckArmoredDetachedSignature performs the same actions as
|
||||
// CheckDetachedSignature but expects the signature to be armored.
|
||||
func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) {
|
||||
body, err := readArmored(signature, SignatureType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return CheckDetachedSignature(keyring, signed, body, config)
|
||||
}
|
||||
|
||||
// checkMessageSignatureDetails returns an error if:
|
||||
// - The signature (or one of the binding signatures mentioned below)
|
||||
// has a unknown critical notation data subpacket
|
||||
// - The primary key of the signing entity is revoked
|
||||
// - The primary identity is revoked
|
||||
// - The signature is expired
|
||||
// - The primary key of the signing entity is expired according to the
|
||||
// primary identity binding signature
|
||||
//
|
||||
// ... or, if the signature was signed by a subkey and:
|
||||
// - The signing subkey is revoked
|
||||
// - The signing subkey is expired according to the subkey binding signature
|
||||
// - The signing subkey binding signature is expired
|
||||
// - The signing subkey cross-signature is expired
|
||||
//
|
||||
// NOTE: The order of these checks is important, as the caller may choose to
|
||||
// ignore ErrSignatureExpired or ErrKeyExpired errors, but should never
|
||||
// ignore any other errors.
|
||||
func checkMessageSignatureDetails(key *Key, signature *packet.Signature, config *packet.Config) error {
|
||||
now := config.Now()
|
||||
primarySelfSignature, primaryIdentity := key.Entity.PrimarySelfSignature()
|
||||
signedBySubKey := key.PublicKey != key.Entity.PrimaryKey
|
||||
sigsToCheck := []*packet.Signature{signature, primarySelfSignature}
|
||||
if signedBySubKey {
|
||||
sigsToCheck = append(sigsToCheck, key.SelfSignature, key.SelfSignature.EmbeddedSignature)
|
||||
}
|
||||
for _, sig := range sigsToCheck {
|
||||
for _, notation := range sig.Notations {
|
||||
if notation.IsCritical && !config.KnownNotation(notation.Name) {
|
||||
return errors.SignatureError("unknown critical notation: " + notation.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if key.Entity.Revoked(now) || // primary key is revoked
|
||||
(signedBySubKey && key.Revoked(now)) || // subkey is revoked
|
||||
(primaryIdentity != nil && primaryIdentity.Revoked(now)) { // primary identity is revoked for v4
|
||||
return errors.ErrKeyRevoked
|
||||
}
|
||||
if key.Entity.PrimaryKey.KeyExpired(primarySelfSignature, now) { // primary key is expired
|
||||
return errors.ErrKeyExpired
|
||||
}
|
||||
if signedBySubKey {
|
||||
if key.PublicKey.KeyExpired(key.SelfSignature, now) { // subkey is expired
|
||||
return errors.ErrKeyExpired
|
||||
}
|
||||
}
|
||||
for _, sig := range sigsToCheck {
|
||||
if sig.SigExpired(now) { // any of the relevant signatures are expired
|
||||
return errors.ErrSignatureExpired
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
457
vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go
generated
vendored
Normal file
457
vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
436
vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go
generated
vendored
Normal file
436
vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go
generated
vendored
Normal file
@ -0,0 +1,436 @@
|
||||
// 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 s2k implements the various OpenPGP string-to-key transforms as
|
||||
// specified in RFC 4800 section 3.7.1, and Argon2 specified in
|
||||
// draft-ietf-openpgp-crypto-refresh-08 section 3.7.1.4.
|
||||
package s2k // import "github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
type Mode uint8
|
||||
|
||||
// Defines the default S2KMode constants
|
||||
//
|
||||
// 0 (simple), 1(salted), 3(iterated), 4(argon2)
|
||||
const (
|
||||
SimpleS2K Mode = 0
|
||||
SaltedS2K Mode = 1
|
||||
IteratedSaltedS2K Mode = 3
|
||||
Argon2S2K Mode = 4
|
||||
GnuS2K Mode = 101
|
||||
)
|
||||
|
||||
const Argon2SaltSize int = 16
|
||||
|
||||
// Params contains all the parameters of the s2k packet
|
||||
type Params struct {
|
||||
// mode is the mode of s2k function.
|
||||
// It can be 0 (simple), 1(salted), 3(iterated)
|
||||
// 2(reserved) 100-110(private/experimental).
|
||||
mode Mode
|
||||
// hashId is the ID of the hash function used in any of the modes
|
||||
hashId byte
|
||||
// salt is a byte array to use as a salt in hashing process or argon2
|
||||
saltBytes [Argon2SaltSize]byte
|
||||
// countByte is used to determine how many rounds of hashing are to
|
||||
// be performed in s2k mode 3. See RFC 4880 Section 3.7.1.3.
|
||||
countByte byte
|
||||
// passes is a parameter in Argon2 to determine the number of iterations
|
||||
// See RFC the crypto refresh Section 3.7.1.4.
|
||||
passes byte
|
||||
// parallelism is a parameter in Argon2 to determine the degree of paralellism
|
||||
// See RFC the crypto refresh Section 3.7.1.4.
|
||||
parallelism byte
|
||||
// memoryExp is a parameter in Argon2 to determine the memory usage
|
||||
// i.e., 2 ** memoryExp kibibytes
|
||||
// See RFC the crypto refresh Section 3.7.1.4.
|
||||
memoryExp byte
|
||||
}
|
||||
|
||||
// encodeCount converts an iterative "count" in the range 1024 to
|
||||
// 65011712, inclusive, to an encoded count. The return value is the
|
||||
// octet that is actually stored in the GPG file. encodeCount panics
|
||||
// if i is not in the above range (encodedCount above takes care to
|
||||
// pass i in the correct range). See RFC 4880 Section 3.7.7.1.
|
||||
func encodeCount(i int) uint8 {
|
||||
if i < 65536 || i > 65011712 {
|
||||
panic("count arg i outside the required range")
|
||||
}
|
||||
|
||||
for encoded := 96; encoded < 256; encoded++ {
|
||||
count := decodeCount(uint8(encoded))
|
||||
if count >= i {
|
||||
return uint8(encoded)
|
||||
}
|
||||
}
|
||||
|
||||
return 255
|
||||
}
|
||||
|
||||
// decodeCount returns the s2k mode 3 iterative "count" corresponding to
|
||||
// the encoded octet c.
|
||||
func decodeCount(c uint8) int {
|
||||
return (16 + int(c&15)) << (uint32(c>>4) + 6)
|
||||
}
|
||||
|
||||
// encodeMemory converts the Argon2 "memory" in the range parallelism*8 to
|
||||
// 2**31, inclusive, to an encoded memory. The return value is the
|
||||
// octet that is actually stored in the GPG file. encodeMemory panics
|
||||
// if is not in the above range
|
||||
// See OpenPGP crypto refresh Section 3.7.1.4.
|
||||
func encodeMemory(memory uint32, parallelism uint8) uint8 {
|
||||
if memory < (8*uint32(parallelism)) || memory > uint32(2147483648) {
|
||||
panic("Memory argument memory is outside the required range")
|
||||
}
|
||||
|
||||
for exp := 3; exp < 31; exp++ {
|
||||
compare := decodeMemory(uint8(exp))
|
||||
if compare >= memory {
|
||||
return uint8(exp)
|
||||
}
|
||||
}
|
||||
|
||||
return 31
|
||||
}
|
||||
|
||||
// decodeMemory computes the decoded memory in kibibytes as 2**memoryExponent
|
||||
func decodeMemory(memoryExponent uint8) uint32 {
|
||||
return uint32(1) << memoryExponent
|
||||
}
|
||||
|
||||
// Simple writes to out the result of computing the Simple S2K function (RFC
|
||||
// 4880, section 3.7.1.1) using the given hash and input passphrase.
|
||||
func Simple(out []byte, h hash.Hash, in []byte) {
|
||||
Salted(out, h, in, nil)
|
||||
}
|
||||
|
||||
var zero [1]byte
|
||||
|
||||
// Salted writes to out the result of computing the Salted S2K function (RFC
|
||||
// 4880, section 3.7.1.2) using the given hash, input passphrase and salt.
|
||||
func Salted(out []byte, h hash.Hash, in []byte, salt []byte) {
|
||||
done := 0
|
||||
var digest []byte
|
||||
|
||||
for i := 0; done < len(out); i++ {
|
||||
h.Reset()
|
||||
for j := 0; j < i; j++ {
|
||||
h.Write(zero[:])
|
||||
}
|
||||
h.Write(salt)
|
||||
h.Write(in)
|
||||
digest = h.Sum(digest[:0])
|
||||
n := copy(out[done:], digest)
|
||||
done += n
|
||||
}
|
||||
}
|
||||
|
||||
// Iterated writes to out the result of computing the Iterated and Salted S2K
|
||||
// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase,
|
||||
// salt and iteration count.
|
||||
func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) {
|
||||
combined := make([]byte, len(in)+len(salt))
|
||||
copy(combined, salt)
|
||||
copy(combined[len(salt):], in)
|
||||
|
||||
if count < len(combined) {
|
||||
count = len(combined)
|
||||
}
|
||||
|
||||
done := 0
|
||||
var digest []byte
|
||||
for i := 0; done < len(out); i++ {
|
||||
h.Reset()
|
||||
for j := 0; j < i; j++ {
|
||||
h.Write(zero[:])
|
||||
}
|
||||
written := 0
|
||||
for written < count {
|
||||
if written+len(combined) > count {
|
||||
todo := count - written
|
||||
h.Write(combined[:todo])
|
||||
written = count
|
||||
} else {
|
||||
h.Write(combined)
|
||||
written += len(combined)
|
||||
}
|
||||
}
|
||||
digest = h.Sum(digest[:0])
|
||||
n := copy(out[done:], digest)
|
||||
done += n
|
||||
}
|
||||
}
|
||||
|
||||
// Argon2 writes to out the key derived from the password (in) with the Argon2
|
||||
// function (the crypto refresh, section 3.7.1.4)
|
||||
func Argon2(out []byte, in []byte, salt []byte, passes uint8, paralellism uint8, memoryExp uint8) {
|
||||
key := argon2.IDKey(in, salt, uint32(passes), decodeMemory(memoryExp), paralellism, uint32(len(out)))
|
||||
copy(out[:], key)
|
||||
}
|
||||
|
||||
// Generate generates valid parameters from given configuration.
|
||||
// It will enforce the Iterated and Salted or Argon2 S2K method.
|
||||
func Generate(rand io.Reader, c *Config) (*Params, error) {
|
||||
var params *Params
|
||||
if c != nil && c.Mode() == Argon2S2K {
|
||||
// handle Argon2 case
|
||||
argonConfig := c.Argon2()
|
||||
params = &Params{
|
||||
mode: Argon2S2K,
|
||||
passes: argonConfig.Passes(),
|
||||
parallelism: argonConfig.Parallelism(),
|
||||
memoryExp: argonConfig.EncodedMemory(),
|
||||
}
|
||||
} else if c != nil && c.PassphraseIsHighEntropy && c.Mode() == SaltedS2K { // Allow SaltedS2K if PassphraseIsHighEntropy
|
||||
hashId, ok := algorithm.HashToHashId(c.hash())
|
||||
if !ok {
|
||||
return nil, errors.UnsupportedError("no such hash")
|
||||
}
|
||||
|
||||
params = &Params{
|
||||
mode: SaltedS2K,
|
||||
hashId: hashId,
|
||||
}
|
||||
} else { // Enforce IteratedSaltedS2K method otherwise
|
||||
hashId, ok := algorithm.HashToHashId(c.hash())
|
||||
if !ok {
|
||||
return nil, errors.UnsupportedError("no such hash")
|
||||
}
|
||||
if c != nil {
|
||||
c.S2KMode = IteratedSaltedS2K
|
||||
}
|
||||
params = &Params{
|
||||
mode: IteratedSaltedS2K,
|
||||
hashId: hashId,
|
||||
countByte: c.EncodedCount(),
|
||||
}
|
||||
}
|
||||
if _, err := io.ReadFull(rand, params.salt()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Parse reads a binary specification for a string-to-key transformation from r
|
||||
// and returns a function which performs that transform. If the S2K is a special
|
||||
// GNU extension that indicates that the private key is missing, then the error
|
||||
// returned is errors.ErrDummyPrivateKey.
|
||||
func Parse(r io.Reader) (f func(out, in []byte), err error) {
|
||||
params, err := ParseIntoParams(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return params.Function()
|
||||
}
|
||||
|
||||
// ParseIntoParams reads a binary specification for a string-to-key
|
||||
// transformation from r and returns a struct describing the s2k parameters.
|
||||
func ParseIntoParams(r io.Reader) (params *Params, err error) {
|
||||
var buf [Argon2SaltSize + 3]byte
|
||||
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params = &Params{
|
||||
mode: Mode(buf[0]),
|
||||
}
|
||||
|
||||
switch params.mode {
|
||||
case SimpleS2K:
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.hashId = buf[0]
|
||||
return params, nil
|
||||
case SaltedS2K:
|
||||
_, err = io.ReadFull(r, buf[:9])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.hashId = buf[0]
|
||||
copy(params.salt(), buf[1:9])
|
||||
return params, nil
|
||||
case IteratedSaltedS2K:
|
||||
_, err = io.ReadFull(r, buf[:10])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.hashId = buf[0]
|
||||
copy(params.salt(), buf[1:9])
|
||||
params.countByte = buf[9]
|
||||
return params, nil
|
||||
case Argon2S2K:
|
||||
_, err = io.ReadFull(r, buf[:Argon2SaltSize+3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(params.salt(), buf[:Argon2SaltSize])
|
||||
params.passes = buf[Argon2SaltSize]
|
||||
params.parallelism = buf[Argon2SaltSize+1]
|
||||
params.memoryExp = buf[Argon2SaltSize+2]
|
||||
if err := validateArgon2Params(params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return params, nil
|
||||
case GnuS2K:
|
||||
// This is a GNU extension. See
|
||||
// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109
|
||||
if _, err = io.ReadFull(r, buf[:5]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.hashId = buf[0]
|
||||
if buf[1] == 'G' && buf[2] == 'N' && buf[3] == 'U' && buf[4] == 1 {
|
||||
return params, nil
|
||||
}
|
||||
return nil, errors.UnsupportedError("GNU S2K extension")
|
||||
}
|
||||
|
||||
return nil, errors.UnsupportedError("S2K function")
|
||||
}
|
||||
|
||||
func (params *Params) Mode() Mode {
|
||||
return params.mode
|
||||
}
|
||||
|
||||
func (params *Params) Dummy() bool {
|
||||
return params != nil && params.mode == GnuS2K
|
||||
}
|
||||
|
||||
func (params *Params) salt() []byte {
|
||||
switch params.mode {
|
||||
case SaltedS2K, IteratedSaltedS2K:
|
||||
return params.saltBytes[:8]
|
||||
case Argon2S2K:
|
||||
return params.saltBytes[:Argon2SaltSize]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (params *Params) Function() (f func(out, in []byte), err error) {
|
||||
if params.Dummy() {
|
||||
return nil, errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
var hashObj crypto.Hash
|
||||
if params.mode != Argon2S2K {
|
||||
var ok bool
|
||||
hashObj, ok = algorithm.HashIdToHashWithSha1(params.hashId)
|
||||
if !ok {
|
||||
return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(params.hashId)))
|
||||
}
|
||||
if !hashObj.Available() {
|
||||
return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashObj)))
|
||||
}
|
||||
}
|
||||
|
||||
switch params.mode {
|
||||
case SimpleS2K:
|
||||
f := func(out, in []byte) {
|
||||
Simple(out, hashObj.New(), in)
|
||||
}
|
||||
|
||||
return f, nil
|
||||
case SaltedS2K:
|
||||
f := func(out, in []byte) {
|
||||
Salted(out, hashObj.New(), in, params.salt())
|
||||
}
|
||||
|
||||
return f, nil
|
||||
case IteratedSaltedS2K:
|
||||
f := func(out, in []byte) {
|
||||
Iterated(out, hashObj.New(), in, params.salt(), decodeCount(params.countByte))
|
||||
}
|
||||
|
||||
return f, nil
|
||||
case Argon2S2K:
|
||||
f := func(out, in []byte) {
|
||||
Argon2(out, in, params.salt(), params.passes, params.parallelism, params.memoryExp)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
return nil, errors.UnsupportedError("S2K function")
|
||||
}
|
||||
|
||||
func (params *Params) Serialize(w io.Writer) (err error) {
|
||||
if _, err = w.Write([]byte{uint8(params.mode)}); err != nil {
|
||||
return
|
||||
}
|
||||
if params.mode != Argon2S2K {
|
||||
if _, err = w.Write([]byte{params.hashId}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if params.Dummy() {
|
||||
_, err = w.Write(append([]byte("GNU"), 1))
|
||||
return
|
||||
}
|
||||
if params.mode > 0 {
|
||||
if _, err = w.Write(params.salt()); err != nil {
|
||||
return
|
||||
}
|
||||
if params.mode == IteratedSaltedS2K {
|
||||
_, err = w.Write([]byte{params.countByte})
|
||||
}
|
||||
if params.mode == Argon2S2K {
|
||||
_, err = w.Write([]byte{params.passes, params.parallelism, params.memoryExp})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize salts and stretches the given passphrase and writes the
|
||||
// resulting key into key. It also serializes an S2K descriptor to
|
||||
// w. The key stretching can be configured with c, which may be
|
||||
// nil. In that case, sensible defaults will be used.
|
||||
func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error {
|
||||
params, err := Generate(rand, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = params.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := params.Function()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f(key, passphrase)
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateArgon2Params checks that the argon2 parameters are valid according to RFC9580.
|
||||
func validateArgon2Params(params *Params) error {
|
||||
// The number of passes t and the degree of parallelism p MUST be non-zero.
|
||||
if params.parallelism == 0 {
|
||||
return errors.StructuralError("invalid argon2 params: parallelism is 0")
|
||||
}
|
||||
if params.passes == 0 {
|
||||
return errors.StructuralError("invalid argon2 params: iterations is 0")
|
||||
}
|
||||
|
||||
// The encoded memory size MUST be a value from 3+ceil(log2(p)) to 31,
|
||||
// such that the decoded memory size m is a value from 8*p to 2^31.
|
||||
if params.memoryExp > 31 || decodeMemory(params.memoryExp) < 8*uint32(params.parallelism) {
|
||||
return errors.StructuralError("invalid argon2 params: memory is out of bounds")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
26
vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go
generated
vendored
Normal file
26
vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package s2k
|
||||
|
||||
// Cache stores keys derived with s2k functions from one passphrase
|
||||
// to avoid recomputation if multiple items are encrypted with
|
||||
// the same parameters.
|
||||
type Cache map[Params][]byte
|
||||
|
||||
// GetOrComputeDerivedKey tries to retrieve the key
|
||||
// for the given s2k parameters from the cache.
|
||||
// If there is no hit, it derives the key with the s2k function from the passphrase,
|
||||
// updates the cache, and returns the key.
|
||||
func (c *Cache) GetOrComputeDerivedKey(passphrase []byte, params *Params, expectedKeySize int) ([]byte, error) {
|
||||
key, found := (*c)[*params]
|
||||
if !found || len(key) != expectedKeySize {
|
||||
var err error
|
||||
derivedKey := make([]byte, expectedKeySize)
|
||||
s2k, err := params.Function()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s2k(derivedKey, passphrase)
|
||||
(*c)[*params] = key
|
||||
return derivedKey, nil
|
||||
}
|
||||
return key, nil
|
||||
}
|
129
vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go
generated
vendored
Normal file
129
vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
package s2k
|
||||
|
||||
import "crypto"
|
||||
|
||||
// Config collects configuration parameters for s2k key-stretching
|
||||
// transformations. A nil *Config is valid and results in all default
|
||||
// values.
|
||||
type Config struct {
|
||||
// S2K (String to Key) mode, used for key derivation in the context of secret key encryption
|
||||
// and passphrase-encrypted data. Either s2k.Argon2S2K or s2k.IteratedSaltedS2K may be used.
|
||||
// If the passphrase is a high-entropy key, indicated by setting PassphraseIsHighEntropy to true,
|
||||
// s2k.SaltedS2K can also be used.
|
||||
// Note: Argon2 is the strongest option but not all OpenPGP implementations are compatible with it
|
||||
//(pending standardisation).
|
||||
// 0 (simple), 1(salted), 3(iterated), 4(argon2)
|
||||
// 2(reserved) 100-110(private/experimental).
|
||||
S2KMode Mode
|
||||
// Only relevant if S2KMode is not set to s2k.Argon2S2K.
|
||||
// Hash is the default hash function to be used. If
|
||||
// nil, SHA256 is used.
|
||||
Hash crypto.Hash
|
||||
// Argon2 parameters for S2K (String to Key).
|
||||
// Only relevant if S2KMode is set to s2k.Argon2S2K.
|
||||
// If nil, default parameters are used.
|
||||
// For more details on the choice of parameters, see https://tools.ietf.org/html/rfc9106#section-4.
|
||||
Argon2Config *Argon2Config
|
||||
// Only relevant if S2KMode is set to s2k.IteratedSaltedS2K.
|
||||
// Iteration count for Iterated S2K (String to Key). It
|
||||
// determines the strength of the passphrase stretching when
|
||||
// the said passphrase is hashed to produce a key. S2KCount
|
||||
// should be between 65536 and 65011712, inclusive. If Config
|
||||
// is nil or S2KCount is 0, the value 16777216 used. Not all
|
||||
// values in the above range can be represented. S2KCount will
|
||||
// be rounded up to the next representable value if it cannot
|
||||
// be encoded exactly. When set, it is strongly encrouraged to
|
||||
// use a value that is at least 65536. See RFC 4880 Section
|
||||
// 3.7.1.3.
|
||||
S2KCount int
|
||||
// Indicates whether the passphrase passed by the application is a
|
||||
// high-entropy key (e.g. it's randomly generated or derived from
|
||||
// another passphrase using a strong key derivation function).
|
||||
// When true, allows the S2KMode to be s2k.SaltedS2K.
|
||||
// When the passphrase is not a high-entropy key, using SaltedS2K is
|
||||
// insecure, and not allowed by draft-ietf-openpgp-crypto-refresh-08.
|
||||
PassphraseIsHighEntropy bool
|
||||
}
|
||||
|
||||
// Argon2Config stores the Argon2 parameters
|
||||
// A nil *Argon2Config is valid and results in all default
|
||||
type Argon2Config struct {
|
||||
NumberOfPasses uint8
|
||||
DegreeOfParallelism uint8
|
||||
// Memory specifies the desired Argon2 memory usage in kibibytes.
|
||||
// For example memory=64*1024 sets the memory cost to ~64 MB.
|
||||
Memory uint32
|
||||
}
|
||||
|
||||
func (c *Config) Mode() Mode {
|
||||
if c == nil {
|
||||
return IteratedSaltedS2K
|
||||
}
|
||||
return c.S2KMode
|
||||
}
|
||||
|
||||
func (c *Config) hash() crypto.Hash {
|
||||
if c == nil || uint(c.Hash) == 0 {
|
||||
return crypto.SHA256
|
||||
}
|
||||
|
||||
return c.Hash
|
||||
}
|
||||
|
||||
func (c *Config) Argon2() *Argon2Config {
|
||||
if c == nil || c.Argon2Config == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Argon2Config
|
||||
}
|
||||
|
||||
// EncodedCount get encoded count
|
||||
func (c *Config) EncodedCount() uint8 {
|
||||
if c == nil || c.S2KCount == 0 {
|
||||
return 224 // The common case. Corresponding to 16777216
|
||||
}
|
||||
|
||||
i := c.S2KCount
|
||||
|
||||
switch {
|
||||
case i < 65536:
|
||||
i = 65536
|
||||
case i > 65011712:
|
||||
i = 65011712
|
||||
}
|
||||
|
||||
return encodeCount(i)
|
||||
}
|
||||
|
||||
func (c *Argon2Config) Passes() uint8 {
|
||||
if c == nil || c.NumberOfPasses == 0 {
|
||||
return 3
|
||||
}
|
||||
return c.NumberOfPasses
|
||||
}
|
||||
|
||||
func (c *Argon2Config) Parallelism() uint8 {
|
||||
if c == nil || c.DegreeOfParallelism == 0 {
|
||||
return 4
|
||||
}
|
||||
return c.DegreeOfParallelism
|
||||
}
|
||||
|
||||
func (c *Argon2Config) EncodedMemory() uint8 {
|
||||
if c == nil || c.Memory == 0 {
|
||||
return 16 // 64 MiB of RAM
|
||||
}
|
||||
|
||||
memory := c.Memory
|
||||
lowerBound := uint32(c.Parallelism()) * 8
|
||||
upperBound := uint32(2147483648)
|
||||
|
||||
switch {
|
||||
case memory < lowerBound:
|
||||
memory = lowerBound
|
||||
case memory > upperBound:
|
||||
memory = upperBound
|
||||
}
|
||||
|
||||
return encodeMemory(memory, c.Parallelism())
|
||||
}
|
620
vendor/github.com/ProtonMail/go-crypto/openpgp/write.go
generated
vendored
Normal file
620
vendor/github.com/ProtonMail/go-crypto/openpgp/write.go
generated
vendored
Normal file
@ -0,0 +1,620 @@
|
||||
// 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"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// DetachSign signs message with the private key from signer (which must
|
||||
// already have been decrypted) and writes the signature to w.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
|
||||
return detachSign(w, signer, message, packet.SigTypeBinary, config)
|
||||
}
|
||||
|
||||
// ArmoredDetachSign signs message with the private key from signer (which
|
||||
// must already have been decrypted) and writes an armored signature to w.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) {
|
||||
return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config)
|
||||
}
|
||||
|
||||
// DetachSignText signs message (after canonicalising the line endings) with
|
||||
// the private key from signer (which must already have been decrypted) and
|
||||
// writes the signature to w.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
|
||||
return detachSign(w, signer, message, packet.SigTypeText, config)
|
||||
}
|
||||
|
||||
// ArmoredDetachSignText signs message (after canonicalising the line endings)
|
||||
// with the private key from signer (which must already have been decrypted)
|
||||
// and writes an armored signature to w.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
|
||||
return armoredDetachSign(w, signer, message, packet.SigTypeText, config)
|
||||
}
|
||||
|
||||
func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
|
||||
out, err := armor.Encode(w, SignatureType, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = detachSign(out, signer, message, sigType, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return out.Close()
|
||||
}
|
||||
|
||||
func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
|
||||
signingKey, ok := signer.SigningKeyById(config.Now(), config.SigningKey())
|
||||
if !ok {
|
||||
return errors.InvalidArgumentError("no valid signing keys")
|
||||
}
|
||||
if signingKey.PrivateKey == nil {
|
||||
return errors.InvalidArgumentError("signing key doesn't have a private key")
|
||||
}
|
||||
if signingKey.PrivateKey.Encrypted {
|
||||
return errors.InvalidArgumentError("signing key is encrypted")
|
||||
}
|
||||
if _, ok := algorithm.HashToHashId(config.Hash()); !ok {
|
||||
return errors.InvalidArgumentError("invalid hash function")
|
||||
}
|
||||
|
||||
sig := createSignaturePacket(signingKey.PublicKey, sigType, config)
|
||||
|
||||
h, err := sig.PrepareSign(config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wrappedHash, err := wrapHashForSignature(h, sig.SigType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = io.Copy(wrappedHash, message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sig.Sign(h, signingKey.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return sig.Serialize(w)
|
||||
}
|
||||
|
||||
// FileHints contains metadata about encrypted files. This metadata is, itself,
|
||||
// encrypted.
|
||||
type FileHints struct {
|
||||
// IsBinary can be set to hint that the contents are binary data.
|
||||
IsBinary bool
|
||||
// FileName hints at the name of the file that should be written. It's
|
||||
// truncated to 255 bytes if longer. It may be empty to suggest that the
|
||||
// file should not be written to disk. It may be equal to "_CONSOLE" to
|
||||
// suggest the data should not be written to disk.
|
||||
FileName string
|
||||
// ModTime contains the modification time of the file, or the zero time if not applicable.
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
|
||||
// The resulting WriteCloser must be closed after the contents of the file have
|
||||
// been written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
if hints == nil {
|
||||
hints = &FileHints{}
|
||||
}
|
||||
|
||||
key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var w io.WriteCloser
|
||||
cipherSuite := packet.CipherSuite{
|
||||
Cipher: config.Cipher(),
|
||||
Mode: config.AEAD().Mode(),
|
||||
}
|
||||
w, err = packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), config.AEAD() != nil, cipherSuite, key, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
literalData := w
|
||||
if algo := config.Compression(); algo != packet.CompressionNone {
|
||||
var compConfig *packet.CompressionConfig
|
||||
if config != nil {
|
||||
compConfig = config.CompressionConfig
|
||||
}
|
||||
literalData, err = packet.SerializeCompressed(w, algo, compConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var epochSeconds uint32
|
||||
if !hints.ModTime.IsZero() {
|
||||
epochSeconds = uint32(hints.ModTime.Unix())
|
||||
}
|
||||
return packet.SerializeLiteral(literalData, hints.IsBinary, hints.FileName, epochSeconds)
|
||||
}
|
||||
|
||||
// intersectPreferences mutates and returns a prefix of a that contains only
|
||||
// the values in the intersection of a and b. The order of a is preserved.
|
||||
func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
|
||||
var j int
|
||||
for _, v := range a {
|
||||
for _, v2 := range b {
|
||||
if v == v2 {
|
||||
a[j] = v
|
||||
j++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a[:j]
|
||||
}
|
||||
|
||||
// intersectPreferences mutates and returns a prefix of a that contains only
|
||||
// the values in the intersection of a and b. The order of a is preserved.
|
||||
func intersectCipherSuites(a [][2]uint8, b [][2]uint8) (intersection [][2]uint8) {
|
||||
var j int
|
||||
for _, v := range a {
|
||||
for _, v2 := range b {
|
||||
if v[0] == v2[0] && v[1] == v2[1] {
|
||||
a[j] = v
|
||||
j++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a[:j]
|
||||
}
|
||||
|
||||
func hashToHashId(h crypto.Hash) uint8 {
|
||||
v, ok := algorithm.HashToHashId(h)
|
||||
if !ok {
|
||||
panic("tried to convert unknown hash")
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// EncryptText encrypts a message to a number of recipients and, optionally,
|
||||
// signs it. Optional information is contained in 'hints', also encrypted, that
|
||||
// aids the recipients in processing the message. The resulting WriteCloser
|
||||
// must be closed after the contents of the file have been written. If config
|
||||
// is nil, sensible defaults will be used. The signing is done in text mode.
|
||||
func EncryptText(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeText, config)
|
||||
}
|
||||
|
||||
// Encrypt encrypts a message to a number of recipients and, optionally, signs
|
||||
// it. hints contains optional information, that is also encrypted, that aids
|
||||
// the recipients in processing the message. The resulting WriteCloser must
|
||||
// be closed after the contents of the file have been written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeBinary, config)
|
||||
}
|
||||
|
||||
// EncryptSplit encrypts a message to a number of recipients and, optionally, signs
|
||||
// it. hints contains optional information, that is also encrypted, that aids
|
||||
// the recipients in processing the message. The resulting WriteCloser must
|
||||
// be closed after the contents of the file have been written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func EncryptSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeBinary, config)
|
||||
}
|
||||
|
||||
// EncryptTextSplit encrypts a message to a number of recipients and, optionally, signs
|
||||
// it. hints contains optional information, that is also encrypted, that aids
|
||||
// the recipients in processing the message. The resulting WriteCloser must
|
||||
// be closed after the contents of the file have been written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func EncryptTextSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeText, config)
|
||||
}
|
||||
|
||||
// writeAndSign writes the data as a payload package and, optionally, signs
|
||||
// it. hints contains optional information, that is also encrypted,
|
||||
// that aids the recipients in processing the message. The resulting
|
||||
// WriteCloser must be closed after the contents of the file have been
|
||||
// written. If config is nil, sensible defaults will be used.
|
||||
func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
var signer *packet.PrivateKey
|
||||
if signed != nil {
|
||||
signKey, ok := signed.SigningKeyById(config.Now(), config.SigningKey())
|
||||
if !ok {
|
||||
return nil, errors.InvalidArgumentError("no valid signing keys")
|
||||
}
|
||||
signer = signKey.PrivateKey
|
||||
if signer == nil {
|
||||
return nil, errors.InvalidArgumentError("no private key in signing key")
|
||||
}
|
||||
if signer.Encrypted {
|
||||
return nil, errors.InvalidArgumentError("signing key must be decrypted")
|
||||
}
|
||||
}
|
||||
|
||||
var hash crypto.Hash
|
||||
for _, hashId := range candidateHashes {
|
||||
if h, ok := algorithm.HashIdToHash(hashId); ok && h.Available() {
|
||||
hash = h
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If the hash specified by config is a candidate, we'll use that.
|
||||
if configuredHash := config.Hash(); configuredHash.Available() {
|
||||
for _, hashId := range candidateHashes {
|
||||
if h, ok := algorithm.HashIdToHash(hashId); ok && h == configuredHash {
|
||||
hash = h
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hash == 0 {
|
||||
hashId := candidateHashes[0]
|
||||
name, ok := algorithm.HashIdToString(hashId)
|
||||
if !ok {
|
||||
name = "#" + strconv.Itoa(int(hashId))
|
||||
}
|
||||
return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
|
||||
}
|
||||
|
||||
var salt []byte
|
||||
if signer != nil {
|
||||
var opsVersion = 3
|
||||
if signer.Version == 6 {
|
||||
opsVersion = signer.Version
|
||||
}
|
||||
ops := &packet.OnePassSignature{
|
||||
Version: opsVersion,
|
||||
SigType: sigType,
|
||||
Hash: hash,
|
||||
PubKeyAlgo: signer.PubKeyAlgo,
|
||||
KeyId: signer.KeyId,
|
||||
IsLast: true,
|
||||
}
|
||||
if opsVersion == 6 {
|
||||
ops.KeyFingerprint = signer.Fingerprint
|
||||
salt, err = packet.SignatureSaltForHash(hash, config.Random())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops.Salt = salt
|
||||
}
|
||||
if err := ops.Serialize(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if hints == nil {
|
||||
hints = &FileHints{}
|
||||
}
|
||||
|
||||
w := payload
|
||||
if signer != nil {
|
||||
// If we need to write a signature packet after the literal
|
||||
// data then we need to stop literalData from closing
|
||||
// encryptedData.
|
||||
w = noOpCloser{w}
|
||||
|
||||
}
|
||||
var epochSeconds uint32
|
||||
if !hints.ModTime.IsZero() {
|
||||
epochSeconds = uint32(hints.ModTime.Unix())
|
||||
}
|
||||
literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if signer != nil {
|
||||
h, wrappedHash, err := hashForSignature(hash, sigType, salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata := &packet.LiteralData{
|
||||
Format: 'u',
|
||||
FileName: hints.FileName,
|
||||
Time: epochSeconds,
|
||||
}
|
||||
if hints.IsBinary {
|
||||
metadata.Format = 'b'
|
||||
}
|
||||
return signatureWriter{payload, literalData, hash, wrappedHash, h, salt, signer, sigType, config, metadata}, nil
|
||||
}
|
||||
return literalData, nil
|
||||
}
|
||||
|
||||
// encrypt encrypts a message to a number of recipients and, optionally, signs
|
||||
// it. hints contains optional information, that is also encrypted, that aids
|
||||
// the recipients in processing the message. The resulting WriteCloser must
|
||||
// be closed after the contents of the file have been written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) {
|
||||
if len(to) == 0 {
|
||||
return nil, errors.InvalidArgumentError("no encryption recipient provided")
|
||||
}
|
||||
|
||||
// These are the possible ciphers that we'll use for the message.
|
||||
candidateCiphers := []uint8{
|
||||
uint8(packet.CipherAES256),
|
||||
uint8(packet.CipherAES128),
|
||||
}
|
||||
|
||||
// These are the possible hash functions that we'll use for the signature.
|
||||
candidateHashes := []uint8{
|
||||
hashToHashId(crypto.SHA256),
|
||||
hashToHashId(crypto.SHA384),
|
||||
hashToHashId(crypto.SHA512),
|
||||
hashToHashId(crypto.SHA3_256),
|
||||
hashToHashId(crypto.SHA3_512),
|
||||
}
|
||||
|
||||
// Prefer GCM if everyone supports it
|
||||
candidateCipherSuites := [][2]uint8{
|
||||
{uint8(packet.CipherAES256), uint8(packet.AEADModeGCM)},
|
||||
{uint8(packet.CipherAES256), uint8(packet.AEADModeEAX)},
|
||||
{uint8(packet.CipherAES256), uint8(packet.AEADModeOCB)},
|
||||
{uint8(packet.CipherAES128), uint8(packet.AEADModeGCM)},
|
||||
{uint8(packet.CipherAES128), uint8(packet.AEADModeEAX)},
|
||||
{uint8(packet.CipherAES128), uint8(packet.AEADModeOCB)},
|
||||
}
|
||||
|
||||
candidateCompression := []uint8{
|
||||
uint8(packet.CompressionNone),
|
||||
uint8(packet.CompressionZIP),
|
||||
uint8(packet.CompressionZLIB),
|
||||
}
|
||||
|
||||
encryptKeys := make([]Key, len(to))
|
||||
|
||||
// AEAD is used only if config enables it and every key supports it
|
||||
aeadSupported := config.AEAD() != nil
|
||||
|
||||
for i := range to {
|
||||
var ok bool
|
||||
encryptKeys[i], ok = to[i].EncryptionKey(config.Now())
|
||||
if !ok {
|
||||
return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no valid encryption keys")
|
||||
}
|
||||
|
||||
primarySelfSignature, _ := to[i].PrimarySelfSignature()
|
||||
if primarySelfSignature == nil {
|
||||
return nil, errors.InvalidArgumentError("entity without a self-signature")
|
||||
}
|
||||
|
||||
if !primarySelfSignature.SEIPDv2 {
|
||||
aeadSupported = false
|
||||
}
|
||||
|
||||
candidateCiphers = intersectPreferences(candidateCiphers, primarySelfSignature.PreferredSymmetric)
|
||||
candidateHashes = intersectPreferences(candidateHashes, primarySelfSignature.PreferredHash)
|
||||
candidateCipherSuites = intersectCipherSuites(candidateCipherSuites, primarySelfSignature.PreferredCipherSuites)
|
||||
candidateCompression = intersectPreferences(candidateCompression, primarySelfSignature.PreferredCompression)
|
||||
}
|
||||
|
||||
// In the event that the intersection of supported algorithms is empty we use the ones
|
||||
// labelled as MUST that every implementation supports.
|
||||
if len(candidateCiphers) == 0 {
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.3
|
||||
candidateCiphers = []uint8{uint8(packet.CipherAES128)}
|
||||
}
|
||||
if len(candidateHashes) == 0 {
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#hash-algos
|
||||
candidateHashes = []uint8{hashToHashId(crypto.SHA256)}
|
||||
}
|
||||
if len(candidateCipherSuites) == 0 {
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.6
|
||||
candidateCipherSuites = [][2]uint8{{uint8(packet.CipherAES128), uint8(packet.AEADModeOCB)}}
|
||||
}
|
||||
|
||||
cipher := packet.CipherFunction(candidateCiphers[0])
|
||||
aeadCipherSuite := packet.CipherSuite{
|
||||
Cipher: packet.CipherFunction(candidateCipherSuites[0][0]),
|
||||
Mode: packet.AEADMode(candidateCipherSuites[0][1]),
|
||||
}
|
||||
|
||||
// If the cipher specified by config is a candidate, we'll use that.
|
||||
configuredCipher := config.Cipher()
|
||||
for _, c := range candidateCiphers {
|
||||
cipherFunc := packet.CipherFunction(c)
|
||||
if cipherFunc == configuredCipher {
|
||||
cipher = cipherFunc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var symKey []byte
|
||||
if aeadSupported {
|
||||
symKey = make([]byte, aeadCipherSuite.Cipher.KeySize())
|
||||
} else {
|
||||
symKey = make([]byte, cipher.KeySize())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(config.Random(), symKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, key := range encryptKeys {
|
||||
if err := packet.SerializeEncryptedKeyAEAD(keyWriter, key.PublicKey, cipher, aeadSupported, symKey, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var payload io.WriteCloser
|
||||
payload, err = packet.SerializeSymmetricallyEncrypted(dataWriter, cipher, aeadSupported, aeadCipherSuite, symKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
payload, err = handleCompression(payload, candidateCompression, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeAndSign(payload, candidateHashes, signed, hints, sigType, config)
|
||||
}
|
||||
|
||||
// Sign signs a message. The resulting WriteCloser must be closed after the
|
||||
// contents of the file have been written. hints contains optional information
|
||||
// that aids the recipients in processing the message.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) {
|
||||
if signed == nil {
|
||||
return nil, errors.InvalidArgumentError("no signer provided")
|
||||
}
|
||||
|
||||
// These are the possible hash functions that we'll use for the signature.
|
||||
candidateHashes := []uint8{
|
||||
hashToHashId(crypto.SHA256),
|
||||
hashToHashId(crypto.SHA384),
|
||||
hashToHashId(crypto.SHA512),
|
||||
hashToHashId(crypto.SHA3_256),
|
||||
hashToHashId(crypto.SHA3_512),
|
||||
}
|
||||
defaultHashes := candidateHashes[0:1]
|
||||
primarySelfSignature, _ := signed.PrimarySelfSignature()
|
||||
if primarySelfSignature == nil {
|
||||
return nil, errors.StructuralError("signed entity has no self-signature")
|
||||
}
|
||||
preferredHashes := primarySelfSignature.PreferredHash
|
||||
if len(preferredHashes) == 0 {
|
||||
preferredHashes = defaultHashes
|
||||
}
|
||||
candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
|
||||
if len(candidateHashes) == 0 {
|
||||
return nil, errors.StructuralError("cannot sign because signing key shares no common algorithms with candidate hashes")
|
||||
}
|
||||
|
||||
return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, packet.SigTypeBinary, config)
|
||||
}
|
||||
|
||||
// signatureWriter hashes the contents of a message while passing it along to
|
||||
// literalData. When closed, it closes literalData, writes a signature packet
|
||||
// to encryptedData and then also closes encryptedData.
|
||||
type signatureWriter struct {
|
||||
encryptedData io.WriteCloser
|
||||
literalData io.WriteCloser
|
||||
hashType crypto.Hash
|
||||
wrappedHash hash.Hash
|
||||
h hash.Hash
|
||||
salt []byte // v6 only
|
||||
signer *packet.PrivateKey
|
||||
sigType packet.SignatureType
|
||||
config *packet.Config
|
||||
metadata *packet.LiteralData // V5 signatures protect document metadata
|
||||
}
|
||||
|
||||
func (s signatureWriter) Write(data []byte) (int, error) {
|
||||
s.wrappedHash.Write(data)
|
||||
switch s.sigType {
|
||||
case packet.SigTypeBinary:
|
||||
return s.literalData.Write(data)
|
||||
case packet.SigTypeText:
|
||||
flag := 0
|
||||
return writeCanonical(s.literalData, data, &flag)
|
||||
}
|
||||
return 0, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(s.sigType)))
|
||||
}
|
||||
|
||||
func (s signatureWriter) Close() error {
|
||||
sig := createSignaturePacket(&s.signer.PublicKey, s.sigType, s.config)
|
||||
sig.Hash = s.hashType
|
||||
sig.Metadata = s.metadata
|
||||
|
||||
if err := sig.SetSalt(s.salt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sig.Sign(s.h, s.signer, s.config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.literalData.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sig.Serialize(s.encryptedData); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.encryptedData.Close()
|
||||
}
|
||||
|
||||
func createSignaturePacket(signer *packet.PublicKey, sigType packet.SignatureType, config *packet.Config) *packet.Signature {
|
||||
sigLifetimeSecs := config.SigLifetime()
|
||||
return &packet.Signature{
|
||||
Version: signer.Version,
|
||||
SigType: sigType,
|
||||
PubKeyAlgo: signer.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
CreationTime: config.Now(),
|
||||
IssuerKeyId: &signer.KeyId,
|
||||
IssuerFingerprint: signer.Fingerprint,
|
||||
Notations: config.Notations(),
|
||||
SigLifetimeSecs: &sigLifetimeSecs,
|
||||
}
|
||||
}
|
||||
|
||||
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
|
||||
// TODO: we have two of these in OpenPGP packages alone. This probably needs
|
||||
// to be promoted somewhere more common.
|
||||
type noOpCloser struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (c noOpCloser) Write(data []byte) (n int, err error) {
|
||||
return c.w.Write(data)
|
||||
}
|
||||
|
||||
func (c noOpCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleCompression(compressed io.WriteCloser, candidateCompression []uint8, config *packet.Config) (data io.WriteCloser, err error) {
|
||||
data = compressed
|
||||
confAlgo := config.Compression()
|
||||
if confAlgo == packet.CompressionNone {
|
||||
return
|
||||
}
|
||||
|
||||
// Set algorithm labelled as MUST as fallback
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.4
|
||||
finalAlgo := packet.CompressionNone
|
||||
// if compression specified by config available we will use it
|
||||
for _, c := range candidateCompression {
|
||||
if uint8(confAlgo) == c {
|
||||
finalAlgo = confAlgo
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if finalAlgo != packet.CompressionNone {
|
||||
var compConfig *packet.CompressionConfig
|
||||
if config != nil {
|
||||
compConfig = config.CompressionConfig
|
||||
}
|
||||
data, err = packet.SerializeCompressed(compressed, finalAlgo, compConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
221
vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go
generated
vendored
Normal file
221
vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
package x25519
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
x25519lib "github.com/cloudflare/circl/dh/x25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
const (
|
||||
hkdfInfo = "OpenPGP X25519"
|
||||
aes128KeySize = 16
|
||||
// The size of a public or private key in bytes.
|
||||
KeySize = x25519lib.Size
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
// Point represents the encoded elliptic curve point of the public key.
|
||||
Point []byte
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
// Secret represents the secret of the private key.
|
||||
Secret []byte
|
||||
}
|
||||
|
||||
// NewPrivateKey creates a new empty private key including the public key.
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates that the provided public key matches the private key.
|
||||
func Validate(pk *PrivateKey) (err error) {
|
||||
var expectedPublicKey, privateKey x25519lib.Key
|
||||
subtle.ConstantTimeCopy(1, privateKey[:], pk.Secret)
|
||||
x25519lib.KeyGen(&expectedPublicKey, &privateKey)
|
||||
if subtle.ConstantTimeCompare(expectedPublicKey[:], pk.PublicKey.Point) == 0 {
|
||||
return errors.KeyInvalidError("x25519: invalid key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a new x25519 key pair.
|
||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||
var privateKey, publicKey x25519lib.Key
|
||||
privateKeyOut := new(PrivateKey)
|
||||
err := generateKey(rand, &privateKey, &publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privateKeyOut.PublicKey.Point = publicKey[:]
|
||||
privateKeyOut.Secret = privateKey[:]
|
||||
return privateKeyOut, nil
|
||||
}
|
||||
|
||||
func generateKey(rand io.Reader, privateKey *x25519lib.Key, publicKey *x25519lib.Key) error {
|
||||
maxRounds := 10
|
||||
isZero := true
|
||||
for round := 0; isZero; round++ {
|
||||
if round == maxRounds {
|
||||
return errors.InvalidArgumentError("x25519: zero keys only, randomness source might be corrupt")
|
||||
}
|
||||
_, err := io.ReadFull(rand, privateKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isZero = constantTimeIsZero(privateKey[:])
|
||||
}
|
||||
x25519lib.KeyGen(publicKey, privateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts a sessionKey with x25519 according to
|
||||
// the OpenPGP crypto refresh specification section 5.1.6. The function assumes that the
|
||||
// sessionKey has the correct format and padding according to the specification.
|
||||
func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) {
|
||||
var ephemeralPrivate, ephemeralPublic, staticPublic, shared x25519lib.Key
|
||||
// Check that the input static public key has 32 bytes
|
||||
if len(publicKey.Point) != KeySize {
|
||||
err = errors.KeyInvalidError("x25519: the public key has the wrong size")
|
||||
return
|
||||
}
|
||||
copy(staticPublic[:], publicKey.Point)
|
||||
// Generate ephemeral keyPair
|
||||
err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Compute shared key
|
||||
ok := x25519lib.Shared(&shared, &ephemeralPrivate, &staticPublic)
|
||||
if !ok {
|
||||
err = errors.KeyInvalidError("x25519: the public key is a low order point")
|
||||
return
|
||||
}
|
||||
// Derive the encryption key from the shared secret
|
||||
encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:])
|
||||
ephemeralPublicKey = &PublicKey{
|
||||
Point: ephemeralPublic[:],
|
||||
}
|
||||
// Encrypt the sessionKey with aes key wrapping
|
||||
encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey)
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt decrypts a session key stored in ciphertext with the provided x25519
|
||||
// private key and ephemeral public key.
|
||||
func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) {
|
||||
var ephemeralPublic, staticPrivate, shared x25519lib.Key
|
||||
// Check that the input ephemeral public key has 32 bytes
|
||||
if len(ephemeralPublicKey.Point) != KeySize {
|
||||
err = errors.KeyInvalidError("x25519: the public key has the wrong size")
|
||||
return
|
||||
}
|
||||
copy(ephemeralPublic[:], ephemeralPublicKey.Point)
|
||||
subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret)
|
||||
// Compute shared key
|
||||
ok := x25519lib.Shared(&shared, &staticPrivate, &ephemeralPublic)
|
||||
if !ok {
|
||||
err = errors.KeyInvalidError("x25519: the ephemeral public key is a low order point")
|
||||
return
|
||||
}
|
||||
// Derive the encryption key from the shared secret
|
||||
encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:])
|
||||
// Decrypt the session key with aes key wrapping
|
||||
encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext)
|
||||
return
|
||||
}
|
||||
|
||||
func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte {
|
||||
inputKey := make([]byte, 3*KeySize)
|
||||
// ephemeral public key | recipient public key | shared secret
|
||||
subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey)
|
||||
subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey)
|
||||
subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret)
|
||||
hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, []byte(hkdfInfo))
|
||||
encryptionKey := make([]byte, aes128KeySize)
|
||||
_, _ = io.ReadFull(hkdfReader, encryptionKey)
|
||||
return encryptionKey
|
||||
}
|
||||
|
||||
func constantTimeIsZero(bytes []byte) bool {
|
||||
isZero := byte(0)
|
||||
for _, b := range bytes {
|
||||
isZero |= b
|
||||
}
|
||||
return isZero == 0
|
||||
}
|
||||
|
||||
// ENCODING/DECODING ciphertexts:
|
||||
|
||||
// EncodeFieldsLength returns the length of the ciphertext encoding
|
||||
// given the encrypted session key.
|
||||
func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int {
|
||||
lenCipherFunction := 0
|
||||
if !v6 {
|
||||
lenCipherFunction = 1
|
||||
}
|
||||
return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction
|
||||
}
|
||||
|
||||
// EncodeField encodes x25519 session key encryption fields as
|
||||
// ephemeral x25519 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey
|
||||
// and writes it to writer.
|
||||
func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) {
|
||||
lenAlgorithm := 0
|
||||
if !v6 {
|
||||
lenAlgorithm = 1
|
||||
}
|
||||
if _, err = writer.Write(ephemeralPublicKey.Point); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil {
|
||||
return err
|
||||
}
|
||||
if !v6 {
|
||||
if _, err = writer.Write([]byte{cipherFunction}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = writer.Write(encryptedSessionKey)
|
||||
return err
|
||||
}
|
||||
|
||||
// DecodeField decodes a x25519 session key encryption as
|
||||
// ephemeral x25519 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey.
|
||||
func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) {
|
||||
var buf [1]byte
|
||||
ephemeralPublicKey = &PublicKey{
|
||||
Point: make([]byte, KeySize),
|
||||
}
|
||||
// 32 octets representing an ephemeral x25519 public key.
|
||||
if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
// A one-octet size of the following fields.
|
||||
if _, err = io.ReadFull(reader, buf[:]); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
followingLen := buf[0]
|
||||
// The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
|
||||
if !v6 {
|
||||
if _, err = io.ReadFull(reader, buf[:]); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
cipherFunction = buf[0]
|
||||
followingLen -= 1
|
||||
}
|
||||
// The encrypted session key.
|
||||
encryptedSessionKey = make([]byte, followingLen)
|
||||
if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil
|
||||
}
|
229
vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go
generated
vendored
Normal file
229
vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
package x448
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
x448lib "github.com/cloudflare/circl/dh/x448"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
const (
|
||||
hkdfInfo = "OpenPGP X448"
|
||||
aes256KeySize = 32
|
||||
// The size of a public or private key in bytes.
|
||||
KeySize = x448lib.Size
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
// Point represents the encoded elliptic curve point of the public key.
|
||||
Point []byte
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
// Secret represents the secret of the private key.
|
||||
Secret []byte
|
||||
}
|
||||
|
||||
// NewPrivateKey creates a new empty private key including the public key.
|
||||
func NewPrivateKey(key PublicKey) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
PublicKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates that the provided public key matches
|
||||
// the private key.
|
||||
func Validate(pk *PrivateKey) (err error) {
|
||||
var expectedPublicKey, privateKey x448lib.Key
|
||||
subtle.ConstantTimeCopy(1, privateKey[:], pk.Secret)
|
||||
x448lib.KeyGen(&expectedPublicKey, &privateKey)
|
||||
if subtle.ConstantTimeCompare(expectedPublicKey[:], pk.PublicKey.Point) == 0 {
|
||||
return errors.KeyInvalidError("x448: invalid key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a new x448 key pair.
|
||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||
var privateKey, publicKey x448lib.Key
|
||||
privateKeyOut := new(PrivateKey)
|
||||
err := generateKey(rand, &privateKey, &publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privateKeyOut.PublicKey.Point = publicKey[:]
|
||||
privateKeyOut.Secret = privateKey[:]
|
||||
return privateKeyOut, nil
|
||||
}
|
||||
|
||||
func generateKey(rand io.Reader, privateKey *x448lib.Key, publicKey *x448lib.Key) error {
|
||||
maxRounds := 10
|
||||
isZero := true
|
||||
for round := 0; isZero; round++ {
|
||||
if round == maxRounds {
|
||||
return errors.InvalidArgumentError("x448: zero keys only, randomness source might be corrupt")
|
||||
}
|
||||
_, err := io.ReadFull(rand, privateKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isZero = constantTimeIsZero(privateKey[:])
|
||||
}
|
||||
x448lib.KeyGen(publicKey, privateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts a sessionKey with x448 according to
|
||||
// the OpenPGP crypto refresh specification section 5.1.7. The function assumes that the
|
||||
// sessionKey has the correct format and padding according to the specification.
|
||||
func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) {
|
||||
var ephemeralPrivate, ephemeralPublic, staticPublic, shared x448lib.Key
|
||||
// Check that the input static public key has 56 bytes.
|
||||
if len(publicKey.Point) != KeySize {
|
||||
err = errors.KeyInvalidError("x448: the public key has the wrong size")
|
||||
return nil, nil, err
|
||||
}
|
||||
copy(staticPublic[:], publicKey.Point)
|
||||
// Generate ephemeral keyPair.
|
||||
if err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Compute shared key.
|
||||
ok := x448lib.Shared(&shared, &ephemeralPrivate, &staticPublic)
|
||||
if !ok {
|
||||
err = errors.KeyInvalidError("x448: the public key is a low order point")
|
||||
return nil, nil, err
|
||||
}
|
||||
// Derive the encryption key from the shared secret.
|
||||
encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:])
|
||||
ephemeralPublicKey = &PublicKey{
|
||||
Point: ephemeralPublic[:],
|
||||
}
|
||||
// Encrypt the sessionKey with aes key wrapping.
|
||||
encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return ephemeralPublicKey, encryptedSessionKey, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts a session key stored in ciphertext with the provided x448
|
||||
// private key and ephemeral public key.
|
||||
func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) {
|
||||
var ephemeralPublic, staticPrivate, shared x448lib.Key
|
||||
// Check that the input ephemeral public key has 56 bytes.
|
||||
if len(ephemeralPublicKey.Point) != KeySize {
|
||||
err = errors.KeyInvalidError("x448: the public key has the wrong size")
|
||||
return nil, err
|
||||
}
|
||||
copy(ephemeralPublic[:], ephemeralPublicKey.Point)
|
||||
subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret)
|
||||
// Compute shared key.
|
||||
ok := x448lib.Shared(&shared, &staticPrivate, &ephemeralPublic)
|
||||
if !ok {
|
||||
err = errors.KeyInvalidError("x448: the ephemeral public key is a low order point")
|
||||
return nil, err
|
||||
}
|
||||
// Derive the encryption key from the shared secret.
|
||||
encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:])
|
||||
// Decrypt the session key with aes key wrapping.
|
||||
encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encodedSessionKey, nil
|
||||
}
|
||||
|
||||
func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte {
|
||||
inputKey := make([]byte, 3*KeySize)
|
||||
// ephemeral public key | recipient public key | shared secret.
|
||||
subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey)
|
||||
subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey)
|
||||
subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret)
|
||||
hkdfReader := hkdf.New(sha512.New, inputKey, []byte{}, []byte(hkdfInfo))
|
||||
encryptionKey := make([]byte, aes256KeySize)
|
||||
_, _ = io.ReadFull(hkdfReader, encryptionKey)
|
||||
return encryptionKey
|
||||
}
|
||||
|
||||
func constantTimeIsZero(bytes []byte) bool {
|
||||
isZero := byte(0)
|
||||
for _, b := range bytes {
|
||||
isZero |= b
|
||||
}
|
||||
return isZero == 0
|
||||
}
|
||||
|
||||
// ENCODING/DECODING ciphertexts:
|
||||
|
||||
// EncodeFieldsLength returns the length of the ciphertext encoding
|
||||
// given the encrypted session key.
|
||||
func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int {
|
||||
lenCipherFunction := 0
|
||||
if !v6 {
|
||||
lenCipherFunction = 1
|
||||
}
|
||||
return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction
|
||||
}
|
||||
|
||||
// EncodeField encodes x448 session key encryption fields as
|
||||
// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey
|
||||
// and writes it to writer.
|
||||
func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) {
|
||||
lenAlgorithm := 0
|
||||
if !v6 {
|
||||
lenAlgorithm = 1
|
||||
}
|
||||
if _, err = writer.Write(ephemeralPublicKey.Point); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil {
|
||||
return err
|
||||
}
|
||||
if !v6 {
|
||||
if _, err = writer.Write([]byte{cipherFunction}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err = writer.Write(encryptedSessionKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeField decodes a x448 session key encryption as
|
||||
// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey.
|
||||
func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) {
|
||||
var buf [1]byte
|
||||
ephemeralPublicKey = &PublicKey{
|
||||
Point: make([]byte, KeySize),
|
||||
}
|
||||
// 56 octets representing an ephemeral x448 public key.
|
||||
if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
// A one-octet size of the following fields.
|
||||
if _, err = io.ReadFull(reader, buf[:]); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
followingLen := buf[0]
|
||||
// The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
|
||||
if !v6 {
|
||||
if _, err = io.ReadFull(reader, buf[:]); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
cipherFunction = buf[0]
|
||||
followingLen -= 1
|
||||
}
|
||||
// The encrypted session key.
|
||||
encryptedSessionKey = make([]byte, followingLen)
|
||||
if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil
|
||||
}
|
Reference in New Issue
Block a user