mirror of https://github.com/go-gitea/gitea
Vendor Update (#16121)
* update github.com/PuerkitoBio/goquery * update github.com/alecthomas/chroma * update github.com/blevesearch/bleve/v2 * update github.com/caddyserver/certmagic * update github.com/go-enry/go-enry/v2 * update github.com/go-git/go-billy/v5 * update github.com/go-git/go-git/v5 * update github.com/go-redis/redis/v8 * update github.com/go-testfixtures/testfixtures/v3 * update github.com/jaytaylor/html2text * update github.com/json-iterator/go * update github.com/klauspost/compress * update github.com/markbates/goth * update github.com/mattn/go-isatty * update github.com/mholt/archiver/v3 * update github.com/microcosm-cc/bluemonday * update github.com/minio/minio-go/v7 * update github.com/prometheus/client_golang * update github.com/unrolled/render * update github.com/xanzy/go-gitlab * update github.com/yuin/goldmark * update github.com/yuin/goldmark-highlighting Co-authored-by: techknowlogick <techknowlogick@gitea.io>pull/16068/head^2
parent
f088dc4ea1
commit
86e2789960
@ -0,0 +1,3 @@ |
||||
# This source code refers to The Go Authors for copyright purposes. |
||||
# The master list of authors is in the main Go distribution, |
||||
# visible at https://tip.golang.org/AUTHORS. |
@ -0,0 +1,3 @@ |
||||
# This source code was written by the Go contributors. |
||||
# The master list of contributors is in the main Go distribution, |
||||
# visible at https://tip.golang.org/CONTRIBUTORS. |
@ -0,0 +1,27 @@ |
||||
Copyright (c) 2009 The Go Authors. All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are |
||||
met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* 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. |
||||
* Neither the name of Google Inc. nor the names of its |
||||
contributors may be used to endorse or promote products derived from |
||||
this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"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 COPYRIGHT |
||||
OWNER OR CONTRIBUTORS 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. |
@ -0,0 +1,22 @@ |
||||
Additional IP Rights Grant (Patents) |
||||
|
||||
"This implementation" means the copyrightable works distributed by |
||||
Google as part of the Go project. |
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive, |
||||
no-charge, royalty-free, irrevocable (except as stated in this section) |
||||
patent license to make, have made, use, offer to sell, sell, import, |
||||
transfer and otherwise run, modify and propagate the contents of this |
||||
implementation of Go, where such license applies only to those patent |
||||
claims, both currently owned or controlled by Google and acquired in |
||||
the future, licensable by Google that are necessarily infringed by this |
||||
implementation of Go. This grant does not include claims that would be |
||||
infringed only as a consequence of further modification of this |
||||
implementation. If you or your agent or exclusive licensee institute or |
||||
order or agree to the institution of patent litigation against any |
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging |
||||
that this implementation of Go or any code incorporated within this |
||||
implementation of Go constitutes direct or contributory patent |
||||
infringement, or inducement of patent infringement, then any patent |
||||
rights granted to you under this License for this implementation of Go |
||||
shall terminate as of the date such litigation is filed. |
@ -0,0 +1,381 @@ |
||||
package bitcurves |
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Copyright 2011 ThePiachu. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bitelliptic implements several Koblitz elliptic curves over prime
|
||||
// fields.
|
||||
|
||||
// This package operates, internally, on Jacobian coordinates. For a given
|
||||
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
|
||||
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
|
||||
// calculation can be performed within the transform (as in ScalarMult and
|
||||
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
|
||||
// reverse the transform than to operate in affine coordinates.
|
||||
|
||||
import ( |
||||
"crypto/elliptic" |
||||
"io" |
||||
"math/big" |
||||
"sync" |
||||
) |
||||
|
||||
// A BitCurve represents a Koblitz Curve with a=0.
|
||||
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
|
||||
type BitCurve struct { |
||||
Name string |
||||
P *big.Int // the order of the underlying field
|
||||
N *big.Int // the order of the base point
|
||||
B *big.Int // the constant of the BitCurve equation
|
||||
Gx, Gy *big.Int // (x,y) of the base point
|
||||
BitSize int // the size of the underlying field
|
||||
} |
||||
|
||||
// Params returns the parameters of the given BitCurve (see BitCurve struct)
|
||||
func (bitCurve *BitCurve) Params() (cp *elliptic.CurveParams) { |
||||
cp = new(elliptic.CurveParams) |
||||
cp.Name = bitCurve.Name |
||||
cp.P = bitCurve.P |
||||
cp.N = bitCurve.N |
||||
cp.Gx = bitCurve.Gx |
||||
cp.Gy = bitCurve.Gy |
||||
cp.BitSize = bitCurve.BitSize |
||||
return cp |
||||
} |
||||
|
||||
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
|
||||
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { |
||||
// y² = x³ + b
|
||||
y2 := new(big.Int).Mul(y, y) //y²
|
||||
y2.Mod(y2, bitCurve.P) //y²%P
|
||||
|
||||
x3 := new(big.Int).Mul(x, x) //x²
|
||||
x3.Mul(x3, x) //x³
|
||||
|
||||
x3.Add(x3, bitCurve.B) //x³+B
|
||||
x3.Mod(x3, bitCurve.P) //(x³+B)%P
|
||||
|
||||
return x3.Cmp(y2) == 0 |
||||
} |
||||
|
||||
// affineFromJacobian reverses the Jacobian transform. See the comment at the
|
||||
// top of the file.
|
||||
func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { |
||||
if z.Cmp(big.NewInt(0)) == 0 { |
||||
panic("bitcurve: Can't convert to affine with Jacobian Z = 0") |
||||
} |
||||
// x = YZ^2 mod P
|
||||
zinv := new(big.Int).ModInverse(z, bitCurve.P) |
||||
zinvsq := new(big.Int).Mul(zinv, zinv) |
||||
|
||||
xOut = new(big.Int).Mul(x, zinvsq) |
||||
xOut.Mod(xOut, bitCurve.P) |
||||
// y = YZ^3 mod P
|
||||
zinvsq.Mul(zinvsq, zinv) |
||||
yOut = new(big.Int).Mul(y, zinvsq) |
||||
yOut.Mod(yOut, bitCurve.P) |
||||
return xOut, yOut |
||||
} |
||||
|
||||
// Add returns the sum of (x1,y1) and (x2,y2)
|
||||
func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { |
||||
z := new(big.Int).SetInt64(1) |
||||
x, y, z := bitCurve.addJacobian(x1, y1, z, x2, y2, z) |
||||
return bitCurve.affineFromJacobian(x, y, z) |
||||
} |
||||
|
||||
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
|
||||
// (x2, y2, z2) and returns their sum, also in Jacobian form.
|
||||
func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { |
||||
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||
z1z1 := new(big.Int).Mul(z1, z1) |
||||
z1z1.Mod(z1z1, bitCurve.P) |
||||
z2z2 := new(big.Int).Mul(z2, z2) |
||||
z2z2.Mod(z2z2, bitCurve.P) |
||||
|
||||
u1 := new(big.Int).Mul(x1, z2z2) |
||||
u1.Mod(u1, bitCurve.P) |
||||
u2 := new(big.Int).Mul(x2, z1z1) |
||||
u2.Mod(u2, bitCurve.P) |
||||
h := new(big.Int).Sub(u2, u1) |
||||
if h.Sign() == -1 { |
||||
h.Add(h, bitCurve.P) |
||||
} |
||||
i := new(big.Int).Lsh(h, 1) |
||||
i.Mul(i, i) |
||||
j := new(big.Int).Mul(h, i) |
||||
|
||||
s1 := new(big.Int).Mul(y1, z2) |
||||
s1.Mul(s1, z2z2) |
||||
s1.Mod(s1, bitCurve.P) |
||||
s2 := new(big.Int).Mul(y2, z1) |
||||
s2.Mul(s2, z1z1) |
||||
s2.Mod(s2, bitCurve.P) |
||||
r := new(big.Int).Sub(s2, s1) |
||||
if r.Sign() == -1 { |
||||
r.Add(r, bitCurve.P) |
||||
} |
||||
r.Lsh(r, 1) |
||||
v := new(big.Int).Mul(u1, i) |
||||
|
||||
x3 := new(big.Int).Set(r) |
||||
x3.Mul(x3, x3) |
||||
x3.Sub(x3, j) |
||||
x3.Sub(x3, v) |
||||
x3.Sub(x3, v) |
||||
x3.Mod(x3, bitCurve.P) |
||||
|
||||
y3 := new(big.Int).Set(r) |
||||
v.Sub(v, x3) |
||||
y3.Mul(y3, v) |
||||
s1.Mul(s1, j) |
||||
s1.Lsh(s1, 1) |
||||
y3.Sub(y3, s1) |
||||
y3.Mod(y3, bitCurve.P) |
||||
|
||||
z3 := new(big.Int).Add(z1, z2) |
||||
z3.Mul(z3, z3) |
||||
z3.Sub(z3, z1z1) |
||||
if z3.Sign() == -1 { |
||||
z3.Add(z3, bitCurve.P) |
||||
} |
||||
z3.Sub(z3, z2z2) |
||||
if z3.Sign() == -1 { |
||||
z3.Add(z3, bitCurve.P) |
||||
} |
||||
z3.Mul(z3, h) |
||||
z3.Mod(z3, bitCurve.P) |
||||
|
||||
return x3, y3, z3 |
||||
} |
||||
|
||||
// Double returns 2*(x,y)
|
||||
func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { |
||||
z1 := new(big.Int).SetInt64(1) |
||||
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1)) |
||||
} |
||||
|
||||
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
|
||||
// returns its double, also in Jacobian form.
|
||||
func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { |
||||
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
|
||||
a := new(big.Int).Mul(x, x) //X1²
|
||||
b := new(big.Int).Mul(y, y) //Y1²
|
||||
c := new(big.Int).Mul(b, b) //B²
|
||||
|
||||
d := new(big.Int).Add(x, b) //X1+B
|
||||
d.Mul(d, d) //(X1+B)²
|
||||
d.Sub(d, a) //(X1+B)²-A
|
||||
d.Sub(d, c) //(X1+B)²-A-C
|
||||
d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C)
|
||||
|
||||
e := new(big.Int).Mul(big.NewInt(3), a) //3*A
|
||||
f := new(big.Int).Mul(e, e) //E²
|
||||
|
||||
x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
|
||||
x3.Sub(f, x3) //F-2*D
|
||||
x3.Mod(x3, bitCurve.P) |
||||
|
||||
y3 := new(big.Int).Sub(d, x3) //D-X3
|
||||
y3.Mul(e, y3) //E*(D-X3)
|
||||
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
|
||||
y3.Mod(y3, bitCurve.P) |
||||
|
||||
z3 := new(big.Int).Mul(y, z) //Y1*Z1
|
||||
z3.Mul(big.NewInt(2), z3) //3*Y1*Z1
|
||||
z3.Mod(z3, bitCurve.P) |
||||
|
||||
return x3, y3, z3 |
||||
} |
||||
|
||||
//TODO: double check if it is okay
|
||||
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
|
||||
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { |
||||
// We have a slight problem in that the identity of the group (the
|
||||
// point at infinity) cannot be represented in (x, y) form on a finite
|
||||
// machine. Thus the standard add/double algorithm has to be tweaked
|
||||
// slightly: our initial state is not the identity, but x, and we
|
||||
// ignore the first true bit in |k|. If we don't find any true bits in
|
||||
// |k|, then we return nil, nil, because we cannot return the identity
|
||||
// element.
|
||||
|
||||
Bz := new(big.Int).SetInt64(1) |
||||
x := Bx |
||||
y := By |
||||
z := Bz |
||||
|
||||
seenFirstTrue := false |
||||
for _, byte := range k { |
||||
for bitNum := 0; bitNum < 8; bitNum++ { |
||||
if seenFirstTrue { |
||||
x, y, z = bitCurve.doubleJacobian(x, y, z) |
||||
} |
||||
if byte&0x80 == 0x80 { |
||||
if !seenFirstTrue { |
||||
seenFirstTrue = true |
||||
} else { |
||||
x, y, z = bitCurve.addJacobian(Bx, By, Bz, x, y, z) |
||||
} |
||||
} |
||||
byte <<= 1 |
||||
} |
||||
} |
||||
|
||||
if !seenFirstTrue { |
||||
return nil, nil |
||||
} |
||||
|
||||
return bitCurve.affineFromJacobian(x, y, z) |
||||
} |
||||
|
||||
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
|
||||
// an integer in big-endian form.
|
||||
func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { |
||||
return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k) |
||||
} |
||||
|
||||
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} |
||||
|
||||
//TODO: double check if it is okay
|
||||
// GenerateKey returns a public/private key pair. The private key is generated
|
||||
// using the given reader, which must return random data.
|
||||
func (bitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) { |
||||
byteLen := (bitCurve.BitSize + 7) >> 3 |
||||
priv = make([]byte, byteLen) |
||||
|
||||
for x == nil { |
||||
_, err = io.ReadFull(rand, priv) |
||||
if err != nil { |
||||
return |
||||
} |
||||
// We have to mask off any excess bits in the case that the size of the
|
||||
// underlying field is not a whole number of bytes.
|
||||
priv[0] &= mask[bitCurve.BitSize%8] |
||||
// This is because, in tests, rand will return all zeros and we don't
|
||||
// want to get the point at infinity and loop forever.
|
||||
priv[1] ^= 0x42 |
||||
x, y = bitCurve.ScalarBaseMult(priv) |
||||
} |
||||
return |
||||
} |
||||
|
||||
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
|
||||
// X9.62.
|
||||
func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte { |
||||
byteLen := (bitCurve.BitSize + 7) >> 3 |
||||
|
||||
ret := make([]byte, 1+2*byteLen) |
||||
ret[0] = 4 // uncompressed point
|
||||
|
||||
xBytes := x.Bytes() |
||||
copy(ret[1+byteLen-len(xBytes):], xBytes) |
||||
yBytes := y.Bytes() |
||||
copy(ret[1+2*byteLen-len(yBytes):], yBytes) |
||||
return ret |
||||
} |
||||
|
||||
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
|
||||
// error, x = nil.
|
||||
func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { |
||||
byteLen := (bitCurve.BitSize + 7) >> 3 |
||||
if len(data) != 1+2*byteLen { |
||||
return |
||||
} |
||||
if data[0] != 4 { // uncompressed form
|
||||
return |
||||
} |
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen]) |
||||
y = new(big.Int).SetBytes(data[1+byteLen:]) |
||||
return |
||||
} |
||||
|
||||
//curve parameters taken from:
|
||||
//http://www.secg.org/collateral/sec2_final.pdf
|
||||
|
||||
var initonce sync.Once |
||||
var secp160k1 *BitCurve |
||||
var secp192k1 *BitCurve |
||||
var secp224k1 *BitCurve |
||||
var secp256k1 *BitCurve |
||||
|
||||
func initAll() { |
||||
initS160() |
||||
initS192() |
||||
initS224() |
||||
initS256() |
||||
} |
||||
|
||||
func initS160() { |
||||
// See SEC 2 section 2.4.1
|
||||
secp160k1 = new(BitCurve) |
||||
secp160k1.Name = "secp160k1" |
||||
secp160k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", 16) |
||||
secp160k1.N, _ = new(big.Int).SetString("0100000000000000000001B8FA16DFAB9ACA16B6B3", 16) |
||||
secp160k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000007", 16) |
||||
secp160k1.Gx, _ = new(big.Int).SetString("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", 16) |
||||
secp160k1.Gy, _ = new(big.Int).SetString("938CF935318FDCED6BC28286531733C3F03C4FEE", 16) |
||||
secp160k1.BitSize = 160 |
||||
} |
||||
|
||||
func initS192() { |
||||
// See SEC 2 section 2.5.1
|
||||
secp192k1 = new(BitCurve) |
||||
secp192k1.Name = "secp192k1" |
||||
secp192k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", 16) |
||||
secp192k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 16) |
||||
secp192k1.B, _ = new(big.Int).SetString("000000000000000000000000000000000000000000000003", 16) |
||||
secp192k1.Gx, _ = new(big.Int).SetString("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", 16) |
||||
secp192k1.Gy, _ = new(big.Int).SetString("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", 16) |
||||
secp192k1.BitSize = 192 |
||||
} |
||||
|
||||
func initS224() { |
||||
// See SEC 2 section 2.6.1
|
||||
secp224k1 = new(BitCurve) |
||||
secp224k1.Name = "secp224k1" |
||||
secp224k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", 16) |
||||
secp224k1.N, _ = new(big.Int).SetString("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 16) |
||||
secp224k1.B, _ = new(big.Int).SetString("00000000000000000000000000000000000000000000000000000005", 16) |
||||
secp224k1.Gx, _ = new(big.Int).SetString("A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", 16) |
||||
secp224k1.Gy, _ = new(big.Int).SetString("7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", 16) |
||||
secp224k1.BitSize = 224 |
||||
} |
||||
|
||||
func initS256() { |
||||
// See SEC 2 section 2.7.1
|
||||
secp256k1 = new(BitCurve) |
||||
secp256k1.Name = "secp256k1" |
||||
secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) |
||||
secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) |
||||
secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) |
||||
secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) |
||||
secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) |
||||
secp256k1.BitSize = 256 |
||||
} |
||||
|
||||
// S160 returns a BitCurve which implements secp160k1 (see SEC 2 section 2.4.1)
|
||||
func S160() *BitCurve { |
||||
initonce.Do(initAll) |
||||
return secp160k1 |
||||
} |
||||
|
||||
// S192 returns a BitCurve which implements secp192k1 (see SEC 2 section 2.5.1)
|
||||
func S192() *BitCurve { |
||||
initonce.Do(initAll) |
||||
return secp192k1 |
||||
} |
||||
|
||||
// S224 returns a BitCurve which implements secp224k1 (see SEC 2 section 2.6.1)
|
||||
func S224() *BitCurve { |
||||
initonce.Do(initAll) |
||||
return secp224k1 |
||||
} |
||||
|
||||
// S256 returns a BitCurve which implements bitcurves (see SEC 2 section 2.7.1)
|
||||
func S256() *BitCurve { |
||||
initonce.Do(initAll) |
||||
return secp256k1 |
||||
} |
@ -0,0 +1,134 @@ |
||||
// Package brainpool implements Brainpool elliptic curves.
|
||||
// Implementation of rcurves is from github.com/ebfe/brainpool
|
||||
// Note that these curves are implemented with naive, non-constant time operations
|
||||
// and are likely not suitable for enviroments where timing attacks are a concern.
|
||||
package brainpool |
||||
|
||||
import ( |
||||
"crypto/elliptic" |
||||
"math/big" |
||||
"sync" |
||||
) |
||||
|
||||
var ( |
||||
once sync.Once |
||||
p256t1, p384t1, p512t1 *elliptic.CurveParams |
||||
p256r1, p384r1, p512r1 *rcurve |
||||
) |
||||
|
||||
func initAll() { |
||||
initP256t1() |
||||
initP384t1() |
||||
initP512t1() |
||||
initP256r1() |
||||
initP384r1() |
||||
initP512r1() |
||||
} |
||||
|
||||
func initP256t1() { |
||||
p256t1 = &elliptic.CurveParams{Name: "brainpoolP256t1"} |
||||
p256t1.P, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16) |
||||
p256t1.N, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16) |
||||
p256t1.B, _ = new(big.Int).SetString("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16) |
||||
p256t1.Gx, _ = new(big.Int).SetString("A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4", 16) |
||||
p256t1.Gy, _ = new(big.Int).SetString("2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE", 16) |
||||
p256t1.BitSize = 256 |
||||
} |
||||
|
||||
func initP256r1() { |
||||
twisted := p256t1 |
||||
params := &elliptic.CurveParams{ |
||||
Name: "brainpoolP256r1", |
||||
P: twisted.P, |
||||
N: twisted.N, |
||||
BitSize: twisted.BitSize, |
||||
} |
||||
params.Gx, _ = new(big.Int).SetString("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16) |
||||
params.Gy, _ = new(big.Int).SetString("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16) |
||||
z, _ := new(big.Int).SetString("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0", 16) |
||||
p256r1 = newrcurve(twisted, params, z) |
||||
} |
||||
|
||||
func initP384t1() { |
||||
p384t1 = &elliptic.CurveParams{Name: "brainpoolP384t1"} |
||||
p384t1.P, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16) |
||||
p384t1.N, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16) |
||||
p384t1.B, _ = new(big.Int).SetString("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16) |
||||
p384t1.Gx, _ = new(big.Int).SetString("18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC", 16) |
||||
p384t1.Gy, _ = new(big.Int).SetString("25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928", 16) |
||||
p384t1.BitSize = 384 |
||||
} |
||||
|
||||
func initP384r1() { |
||||
twisted := p384t1 |
||||
params := &elliptic.CurveParams{ |
||||
Name: "brainpoolP384r1", |
||||
P: twisted.P, |
||||
N: twisted.N, |
||||
BitSize: twisted.BitSize, |
||||
} |
||||
params.Gx, _ = new(big.Int).SetString("1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", 16) |
||||
params.Gy, _ = new(big.Int).SetString("8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", 16) |
||||
z, _ := new(big.Int).SetString("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C", 16) |
||||
p384r1 = newrcurve(twisted, params, z) |
||||
} |
||||
|
||||
func initP512t1() { |
||||
p512t1 = &elliptic.CurveParams{Name: "brainpoolP512t1"} |
||||
p512t1.P, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16) |
||||
p512t1.N, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16) |
||||
p512t1.B, _ = new(big.Int).SetString("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16) |
||||
p512t1.Gx, _ = new(big.Int).SetString("640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA", 16) |
||||
p512t1.Gy, _ = new(big.Int).SetString("5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332", 16) |
||||
p512t1.BitSize = 512 |
||||
} |
||||
|
||||
func initP512r1() { |
||||
twisted := p512t1 |
||||
params := &elliptic.CurveParams{ |
||||
Name: "brainpoolP512r1", |
||||
P: twisted.P, |
||||
N: twisted.N, |
||||
BitSize: twisted.BitSize, |
||||
} |
||||
params.Gx, _ = new(big.Int).SetString("81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", 16) |
||||
params.Gy, _ = new(big.Int).SetString("7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", 16) |
||||
z, _ := new(big.Int).SetString("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB", 16) |
||||
p512r1 = newrcurve(twisted, params, z) |
||||
} |
||||
|
||||
// P256t1 returns a Curve which implements Brainpool P256t1 (see RFC 5639, section 3.4)
|
||||
func P256t1() elliptic.Curve { |
||||
once.Do(initAll) |
||||
return p256t1 |
||||
} |
||||
|
||||
// P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4)
|
||||
func P256r1() elliptic.Curve { |
||||
once.Do(initAll) |
||||
return p256r1 |
||||
} |
||||
|
||||
// P384t1 returns a Curve which implements Brainpool P384t1 (see RFC 5639, section 3.6)
|
||||
func P384t1() elliptic.Curve { |
||||
once.Do(initAll) |
||||
return p384t1 |
||||
} |
||||
|
||||
// P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6)
|
||||
func P384r1() elliptic.Curve { |
||||
once.Do(initAll) |
||||
return p384r1 |
||||
} |
||||
|
||||
// P512t1 returns a Curve which implements Brainpool P512t1 (see RFC 5639, section 3.7)
|
||||
func P512t1() elliptic.Curve { |
||||
once.Do(initAll) |
||||
return p512t1 |
||||
} |
||||
|
||||
// P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7)
|
||||
func P512r1() elliptic.Curve { |
||||
once.Do(initAll) |
||||
return p512r1 |
||||
} |
@ -0,0 +1,83 @@ |
||||
package brainpool |
||||
|
||||
import ( |
||||
"crypto/elliptic" |
||||
"math/big" |
||||
) |
||||
|
||||
var _ elliptic.Curve = (*rcurve)(nil) |
||||
|
||||
type rcurve struct { |
||||
twisted elliptic.Curve |
||||
params *elliptic.CurveParams |
||||
z *big.Int |
||||
zinv *big.Int |
||||
z2 *big.Int |
||||
z3 *big.Int |
||||
zinv2 *big.Int |
||||
zinv3 *big.Int |
||||
} |
||||
|
||||
var ( |
||||
two = big.NewInt(2) |
||||
three = big.NewInt(3) |
||||
) |
||||
|
||||
func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve { |
||||
zinv := new(big.Int).ModInverse(z, params.P) |
||||
return &rcurve{ |
||||
twisted: twisted, |
||||
params: params, |
||||
z: z, |
||||
zinv: zinv, |
||||
z2: new(big.Int).Exp(z, two, params.P), |
||||
z3: new(big.Int).Exp(z, three, params.P), |
||||
zinv2: new(big.Int).Exp(zinv, two, params.P), |
||||
zinv3: new(big.Int).Exp(zinv, three, params.P), |
||||
} |
||||
} |
||||
|
||||
func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) { |
||||
var tx, ty big.Int |
||||
tx.Mul(x, curve.z2) |
||||
tx.Mod(&tx, curve.params.P) |
||||
ty.Mul(y, curve.z3) |
||||
ty.Mod(&ty, curve.params.P) |
||||
return &tx, &ty |
||||
} |
||||
|
||||
func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) { |
||||
var x, y big.Int |
||||
x.Mul(tx, curve.zinv2) |
||||
x.Mod(&x, curve.params.P) |
||||
y.Mul(ty, curve.zinv3) |
||||
y.Mod(&y, curve.params.P) |
||||
return &x, &y |
||||
} |
||||
|
||||
func (curve *rcurve) Params() *elliptic.CurveParams { |
||||
return curve.params |
||||
} |
||||
|
||||
func (curve *rcurve) IsOnCurve(x, y *big.Int) bool { |
||||
return curve.twisted.IsOnCurve(curve.toTwisted(x, y)) |
||||
} |
||||
|
||||
func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { |
||||
tx1, ty1 := curve.toTwisted(x1, y1) |
||||
tx2, ty2 := curve.toTwisted(x2, y2) |
||||
return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2)) |
||||
} |
||||
|
||||
func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) { |
||||
return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1))) |
||||
} |
||||
|
||||
func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) { |
||||
tx1, ty1 := curve.toTwisted(x1, y1) |
||||
return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar)) |
||||
} |
||||
|
||||
func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { |
||||
return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar)) |
||||
} |
@ -0,0 +1,162 @@ |
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
// Package eax provides an implementation of the EAX
|
||||
// (encrypt-authenticate-translate) mode of operation, as described in
|
||||
// Bellare, Rogaway, and Wagner "THE EAX MODE OF OPERATION: A TWO-PASS
|
||||
// AUTHENTICATED-ENCRYPTION SCHEME OPTIMIZED FOR SIMPLICITY AND EFFICIENCY."
|
||||
// In FSE'04, volume 3017 of LNCS, 2004
|
||||
package eax |
||||
|
||||
import ( |
||||
"crypto/cipher" |
||||
"crypto/subtle" |
||||
"errors" |
||||
"github.com/ProtonMail/go-crypto/internal/byteutil" |
||||
) |
||||
|
||||
const ( |
||||
defaultTagSize = 16 |
||||
defaultNonceSize = 16 |
||||
) |
||||
|
||||
type eax struct { |
||||
block cipher.Block // Only AES-{128, 192, 256} supported
|
||||
tagSize int // At least 12 bytes recommended
|
||||
nonceSize int |
||||
} |
||||
|
||||
func (e *eax) NonceSize() int { |
||||
return e.nonceSize |
||||
} |
||||
|
||||
func (e *eax) Overhead() int { |
||||
return e.tagSize |
||||
} |
||||
|
||||
// NewEAX returns an EAX instance with AES-{KEYLENGTH} and default nonce and
|
||||
// tag lengths. Supports {128, 192, 256}- bit key length.
|
||||
func NewEAX(block cipher.Block) (cipher.AEAD, error) { |
||||
return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize) |
||||
} |
||||
|
||||
// NewEAXWithNonceAndTagSize returns an EAX instance with AES-{keyLength} and
|
||||
// given nonce and tag lengths in bytes. Panics on zero nonceSize and
|
||||
// exceedingly long tags.
|
||||
//
|
||||
// It is recommended to use at least 12 bytes as tag length (see, for instance,
|
||||
// NIST SP 800-38D).
|
||||
//
|
||||
// Only to be used for compatibility with existing cryptosystems with
|
||||
// non-standard parameters. For all other cases, prefer NewEAX.
|
||||
func NewEAXWithNonceAndTagSize( |
||||
block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) { |
||||
if nonceSize < 1 { |
||||
return nil, eaxError("Cannot initialize EAX with nonceSize = 0") |
||||
} |
||||
if tagSize > block.BlockSize() { |
||||
return nil, eaxError("Custom tag length exceeds blocksize") |
||||
} |
||||
return &eax{ |
||||
block: block, |
||||
tagSize: tagSize, |
||||
nonceSize: nonceSize, |
||||
}, nil |
||||
} |
||||
|
||||
func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte { |
||||
if len(nonce) > e.nonceSize { |
||||
panic("crypto/eax: Nonce too long for this instance") |
||||
} |
||||
ret, out := byteutil.SliceForAppend(dst, len(plaintext) + e.tagSize) |
||||
omacNonce := e.omacT(0, nonce) |
||||
omacAdata := e.omacT(1, adata) |
||||
|
||||
// Encrypt message using CTR mode and omacNonce as IV
|
||||
ctr := cipher.NewCTR(e.block, omacNonce) |
||||
ciphertextData := out[:len(plaintext)] |
||||
ctr.XORKeyStream(ciphertextData, plaintext) |
||||
|
||||
omacCiphertext := e.omacT(2, ciphertextData) |
||||
|
||||
tag := out[len(plaintext):] |
||||
for i := 0; i < e.tagSize; i++ { |
||||
tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i] |
||||
} |
||||
return ret |
||||
} |
||||
|
||||
func (e* eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) { |
||||
if len(nonce) > e.nonceSize { |
||||
panic("crypto/eax: Nonce too long for this instance") |
||||
} |
||||
if len(ciphertext) < e.tagSize { |
||||
return nil, eaxError("Ciphertext shorter than tag length") |
||||
} |
||||
sep := len(ciphertext) - e.tagSize |
||||
|
||||
// Compute tag
|
||||
omacNonce := e.omacT(0, nonce) |
||||
omacAdata := e.omacT(1, adata) |
||||
omacCiphertext := e.omacT(2, ciphertext[:sep]) |
||||
|
||||
tag := make([]byte, e.tagSize) |
||||
for i := 0; i < e.tagSize; i++ { |
||||
tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i] |
||||
} |
||||
|
||||
// Compare tags
|
||||
if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 { |
||||
return nil, eaxError("Tag authentication failed") |
||||
} |
||||
|
||||
// Decrypt ciphertext
|
||||
ret, out := byteutil.SliceForAppend(dst, len(ciphertext)) |
||||
ctr := cipher.NewCTR(e.block, omacNonce) |
||||
ctr.XORKeyStream(out, ciphertext[:sep]) |
||||
|
||||
return ret[:sep], nil |
||||
} |
||||
|
||||
// Tweakable OMAC - Calls OMAC_K([t]_n || plaintext)
|
||||
func (e *eax) omacT(t byte, plaintext []byte) []byte { |
||||
blockSize := e.block.BlockSize() |
||||
byteT := make([]byte, blockSize) |
||||
byteT[blockSize-1] = t |
||||
concat := append(byteT, plaintext...) |
||||
return e.omac(concat) |
||||
} |
||||
|
||||
func (e *eax) omac(plaintext []byte) []byte { |
||||
blockSize := e.block.BlockSize() |
||||
// L ← E_K(0^n); B ← 2L; P ← 4L
|
||||
L := make([]byte, blockSize) |
||||
e.block.Encrypt(L, L) |
||||
B := byteutil.GfnDouble(L) |
||||
P := byteutil.GfnDouble(B) |
||||
|
||||
// CBC with IV = 0
|
||||
cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize)) |
||||
padded := e.pad(plaintext, B, P) |
||||
cbcCiphertext := make([]byte, len(padded)) |
||||
cbc.CryptBlocks(cbcCiphertext, padded) |
||||
|
||||
return cbcCiphertext[len(cbcCiphertext)-blockSize:] |
||||
} |
||||
|
||||
func (e *eax) pad(plaintext, B, P []byte) []byte { |
||||
// if |M| in {n, 2n, 3n, ...}
|
||||
blockSize := e.block.BlockSize() |
||||
if len(plaintext) != 0 && len(plaintext)%blockSize == 0 { |
||||
return byteutil.RightXor(plaintext, B) |
||||
} |
||||
|
||||
// else return (M || 1 || 0^(n−1−(|M| % n))) xor→ P
|
||||
ending := make([]byte, blockSize-len(plaintext)%blockSize) |
||||
ending[0] = 0x80 |
||||
padded := append(plaintext, ending...) |
||||
return byteutil.RightXor(padded, P) |
||||
} |
||||
|
||||
func eaxError(err string) error { |
||||
return errors.New("crypto/eax: " + err) |
||||
} |
@ -0,0 +1,58 @@ |
||||
package eax |
||||
|
||||
// Test vectors from
|
||||
// https://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf
|
||||
var testVectors = []struct { |
||||
msg, key, nonce, header, ciphertext string |
||||
}{ |
||||
{"", |
||||
"233952DEE4D5ED5F9B9C6D6FF80FF478", |
||||
"62EC67F9C3A4A407FCB2A8C49031A8B3", |
||||
"6BFB914FD07EAE6B", |
||||
"E037830E8389F27B025A2D6527E79D01"}, |
||||
{"F7FB", |
||||
"91945D3F4DCBEE0BF45EF52255F095A4", |
||||
"BECAF043B0A23D843194BA972C66DEBD", |
||||
"FA3BFD4806EB53FA", |
||||
"19DD5C4C9331049D0BDAB0277408F67967E5"}, |
||||
{"1A47CB4933", |
||||
"01F74AD64077F2E704C0F60ADA3DD523", |
||||
"70C3DB4F0D26368400A10ED05D2BFF5E", |
||||
"234A3463C1264AC6", |
||||
"D851D5BAE03A59F238A23E39199DC9266626C40F80"}, |
||||
{"481C9E39B1", |
||||
"D07CF6CBB7F313BDDE66B727AFD3C5E8", |
||||
"8408DFFF3C1A2B1292DC199E46B7D617", |
||||
"33CCE2EABFF5A79D", |
||||
"632A9D131AD4C168A4225D8E1FF755939974A7BEDE"}, |
||||
{"40D0C07DA5E4", |
||||
"35B6D0580005BBC12B0587124557D2C2", |
||||
"FDB6B06676EEDC5C61D74276E1F8E816", |
||||
"AEB96EAEBE2970E9", |
||||
"071DFE16C675CB0677E536F73AFE6A14B74EE49844DD"}, |
||||
{"4DE3B35C3FC039245BD1FB7D", |
||||
"BD8E6E11475E60B268784C38C62FEB22", |
||||
"6EAC5C93072D8E8513F750935E46DA1B", |
||||
"D4482D1CA78DCE0F", |
||||
"835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F"}, |
||||
{"8B0A79306C9CE7ED99DAE4F87F8DD61636", |
||||
"7C77D6E813BED5AC98BAA417477A2E7D", |
||||
"1A8C98DCD73D38393B2BF1569DEEFC19", |
||||
"65D2017990D62528", |
||||
"02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2"}, |
||||
{"1BDA122BCE8A8DBAF1877D962B8592DD2D56", |
||||
"5FFF20CAFAB119CA2FC73549E20F5B0D", |
||||
"DDE59B97D722156D4D9AFF2BC7559826", |
||||
"54B9F04E6A09189A", |
||||
"2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A"}, |
||||
{"6CF36720872B8513F6EAB1A8A44438D5EF11", |
||||
"A4A4782BCFFD3EC5E7EF6D8C34A56123", |
||||
"B781FCF2F75FA5A8DE97A9CA48E522EC", |
||||
"899A175897561D7E", |
||||
"0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700"}, |
||||
{"CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7", |
||||
"8395FCF1E95BEBD697BD010BC766AAC3", |
||||
"22E7ADD93CFC6393C57EC0B3C17D6B44", |
||||
"126735FCC320D25A", |
||||
"CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E"}, |
||||
} |
@ -0,0 +1,131 @@ |
||||
// These vectors include key length in {128, 192, 256}, tag size 128, and
|
||||
// random nonce, header, and plaintext lengths.
|
||||
|
||||
// This file was automatically generated.
|
||||
|
||||
package eax |
||||
|
||||
var randomVectors = []struct { |
||||
key, nonce, header, plaintext, ciphertext string |
||||
}{ |
||||
{"DFDE093F36B0356E5A81F609786982E3", |
||||
"1D8AC604419001816905BA72B14CED7E", |
||||
"152A1517A998D7A24163FCDD146DE81AC347C8B97088F502093C1ABB8F6E33D9A219C34D7603A18B1F5ABE02E56661B7D7F67E81EC08C1302EF38D80A859486D450E94A4F26AD9E68EEBBC0C857A0FC5CF9E641D63D565A7E361BC8908F5A8DC8FD6", |
||||
"1C8EAAB71077FE18B39730A3156ADE29C5EE824C7EE86ED2A253B775603FB237116E654F6FEC588DD27F523A0E01246FE73FE348491F2A8E9ABC6CA58D663F71CDBCF4AD798BE46C42AE6EE8B599DB44A1A48D7BBBBA0F7D2750181E1C5E66967F7D57CBD30AFBDA5727", |
||||
"79E7E150934BBEBF7013F61C60462A14D8B15AF7A248AFB8A344EF021C1500E16666891D6E973D8BB56B71A371F12CA34660C4410C016982B20F547E3762A58B7BF4F20236CADCF559E2BE7D783B13723B2741FC7CDC8997D839E39A3DDD2BADB96743DD7049F1BDB0516A262869915B3F70498AFB7B191BF960"}, |
||||
{"F10619EF02E5D94D7550EB84ED364A21", |
||||
"8DC0D4F2F745BBAE835CC5574B942D20", |
||||
"FE561358F2E8DF7E1024FF1AE9A8D36EBD01352214505CB99D644777A8A1F6027FA2BDBFC529A9B91136D5F2416CFC5F0F4EC3A1AFD32BDDA23CA504C5A5CB451785FABF4DFE4CD50D817491991A60615B30286361C100A95D1712F2A45F8E374461F4CA2B", |
||||
"D7B5A971FC219631D30EFC3664AE3127D9CF3097DAD9C24AC7905D15E8D9B25B026B31D68CAE00975CDB81EB1FD96FD5E1A12E2BB83FA25F1B1D91363457657FC03875C27F2946C5", |
||||
"2F336ED42D3CC38FC61660C4CD60BA4BD438B05F5965D8B7B399D2E7167F5D34F792D318F94DB15D67463AC449E13D568CC09BFCE32A35EE3EE96A041927680AE329811811E27F2D1E8E657707AF99BA96D13A478D695D59"}, |
||||
{"429F514EFC64D98A698A9247274CFF45", |
||||
"976AA5EB072F912D126ACEBC954FEC38", |
||||
"A71D89DC5B6CEDBB7451A27C3C2CAE09126DB4C421", |
||||
"5632FE62AB1DC549D54D3BC3FC868ACCEDEFD9ECF5E9F8", |
||||
"848AE4306CA8C7F416F8707625B7F55881C0AB430353A5C967CDA2DA787F581A70E34DBEBB2385"}, |
||||
{"398138F309085F47F8457CDF53895A63", |
||||
"F8A8A7F2D28E5FFF7BBC2F24353F7A36", |
||||
"5D633C21BA7764B8855CAB586F3746E236AD486039C83C6B56EFA9C651D38A41D6B20DAEE3418BFEA44B8BD6", |
||||
"A3BBAA91920AF5E10659818B1B3B300AC79BFC129C8329E75251F73A66D3AE0128EB91D5031E0A65C329DB7D1E9C0493E268", |
||||
"D078097267606E5FB07CFB7E2B4B718172A82C6A4CEE65D549A4DFB9838003BD2FBF64A7A66988AC1A632FD88F9E9FBB57C5A78AD2E086EACBA3DB68511D81C2970A"}, |
||||
{"7A4151EBD3901B42CBA45DAFB2E931BA", |
||||
"0FC88ACEE74DD538040321C330974EB8", |
||||
"250464FB04733BAB934C59E6AD2D6AE8D662CBCFEFBE61E5A308D4211E58C4C25935B72C69107722E946BFCBF416796600542D76AEB73F2B25BF53BAF97BDEB36ED3A7A51C31E7F170EB897457E7C17571D1BA0A908954E9", |
||||
"88C41F3EBEC23FAB8A362D969CAC810FAD4F7CA6A7F7D0D44F060F92E37E1183768DD4A8C733F71C96058D362A39876D183B86C103DE", |
||||
"74A25B2182C51096D48A870D80F18E1CE15867778E34FCBA6BD7BFB3739FDCD42AD0F2D9F4EBA29085285C6048C15BCE5E5166F1F962D3337AA88E6062F05523029D0A7F0BF9"}, |
||||
{"BFB147E1CD5459424F8C0271FC0E0DC5", |
||||
"EABCC126442BF373969EA3015988CC45", |
||||
"4C0880E1D71AA2C7", |
||||
"BE1B5EC78FBF73E7A6682B21BA7E0E5D2D1C7ABE", |
||||
"5660D7C1380E2F306895B1402CB2D6C37876504276B414D120F4CF92FDDDBB293A238EA0"}, |
||||
{"595DD6F52D18BC2CA8EB4EDAA18D9FA3", |
||||
"0F84B5D36CF4BC3B863313AF3B4D2E97", |
||||
"30AE6CC5F99580F12A779D98BD379A60948020C0B6FBD5746B30BA3A15C6CD33DAF376C70A9F15B6C0EB410A93161F7958AE23", |
||||
"8EF3687A1642B070970B0B91462229D1D76ABC154D18211F7152AA9FF368", |
||||
"317C1DDB11417E5A9CC4DDE7FDFF6659A5AC4B31DE025212580A05CDAC6024D3E4AE7C2966E52B9129E9ECDBED86"}, |
||||
{"44E6F2DC8FDC778AD007137D11410F50", |
||||
"270A237AD977F7187AA6C158A0BAB24F", |
||||
"509B0F0EB12E2AA5C5BA2DE553C07FAF4CE0C9E926531AA709A3D6224FCB783ACCF1559E10B1123EBB7D52E8AB54E6B5352A9ED0D04124BF0E9D9BACFD7E32B817B2E625F5EE94A64EDE9E470DE7FE6886C19B294F9F828209FE257A78", |
||||
"8B3D7815DF25618A5D0C55A601711881483878F113A12EC36CF64900549A3199555528559DC118F789788A55FAFD944E6E99A9CA3F72F238CD3F4D88223F7A745992B3FAED1848", |
||||
"1CC00D79F7AD82FDA71B58D286E5F34D0CC4CEF30704E771CC1E50746BDF83E182B078DB27149A42BAE619DF0F85B0B1090AD55D3B4471B0D6F6ECCD09C8F876B30081F0E7537A9624F8AAF29DA85E324122EFB4D68A56"}, |
||||
{"BB7BC352A03044B4428D8DBB4B0701FDEC4649FD17B81452", |
||||
"8B4BBE26CCD9859DCD84884159D6B0A4", |
||||
"2212BEB0E78E0F044A86944CF33C8D5C80D9DBE1034BF3BCF73611835C7D3A52F5BD2D81B68FD681B68540A496EE5DA16FD8AC8824E60E1EC2042BE28FB0BFAD4E4B03596446BDD8C37D936D9B3D5295BE19F19CF5ACE1D33A46C952CE4DE5C12F92C1DD051E04AEED", |
||||
"9037234CC44FFF828FABED3A7084AF40FA7ABFF8E0C0EFB57A1CC361E18FC4FAC1AB54F3ABFE9FF77263ACE16C3A", |
||||
"A9391B805CCD956081E0B63D282BEA46E7025126F1C1631239C33E92AA6F92CD56E5A4C56F00FF9658E93D48AF4EF0EF81628E34AD4DB0CDAEDCD2A17EE7"}, |
||||
{"99C0AD703196D2F60A74E6B378B838B31F82EA861F06FC4E", |
||||
"92745C018AA708ECFEB1667E9F3F1B01", |
||||
"828C69F376C0C0EC651C67749C69577D589EE39E51404D80EBF70C8660A8F5FD375473F4A7C611D59CB546A605D67446CE2AA844135FCD78BB5FBC90222A00D42920BB1D7EEDFB0C4672554F583EF23184F89063CDECBE482367B5F9AF3ACBC3AF61392BD94CBCD9B64677", |
||||
"A879214658FD0A5B0E09836639BF82E05EC7A5EF71D4701934BDA228435C68AC3D5CEB54997878B06A655EEACEFB1345C15867E7FE6C6423660C8B88DF128EBD6BCD85118DBAE16E9252FFB204324E5C8F38CA97759BDBF3CB0083", |
||||
"51FE87996F194A2585E438B023B345439EA60D1AEBED4650CDAF48A4D4EEC4FC77DC71CC4B09D3BEEF8B7B7AF716CE2B4EFFB3AC9E6323C18AC35E0AA6E2BBBC8889490EB6226C896B0D105EAB42BFE7053CCF00ED66BA94C1BA09A792AA873F0C3B26C5C5F9A936E57B25"}, |
||||
{"7086816D00D648FB8304AA8C9E552E1B69A9955FB59B25D1", |
||||
"0F45CF7F0BF31CCEB85D9DA10F4D749F", |
||||
"93F27C60A417D9F0669E86ACC784FC8917B502DAF30A6338F11B30B94D74FEFE2F8BE1BBE2EAD10FAB7EED3C6F72B7C3ECEE1937C32ED4970A6404E139209C05", |
||||
"877F046601F3CBE4FB1491943FA29487E738F94B99AF206262A1D6FF856C9AA0B8D4D08A54370C98F8E88FA3DCC2B14C1F76D71B2A4C7963AEE8AF960464C5BEC8357AD00DC8", |
||||
"FE96906B895CE6A8E72BC72344E2C8BB3C63113D70EAFA26C299BAFE77A8A6568172EB447FB3E86648A0AF3512DEB1AAC0819F3EC553903BF28A9FB0F43411237A774BF9EE03E445D280FBB9CD12B9BAAB6EF5E52691"}, |
||||
{"062F65A896D5BF1401BADFF70E91B458E1F9BD4888CB2E4D", |
||||
"5B11EA1D6008EBB41CF892FCA5B943D1", |
||||
"BAF4FF5C8242", |
||||
"A8870E091238355984EB2F7D61A865B9170F440BFF999A5993DD41A10F4440D21FF948DDA2BF663B2E03AC3324492DC5E40262ECC6A65C07672353BE23E7FB3A9D79FF6AA38D97960905A38DECC312CB6A59E5467ECF06C311CD43ADC0B543EDF34FE8BE611F176460D5627CA51F8F8D9FED71F55C", |
||||
"B10E127A632172CF8AA7539B140D2C9C2590E6F28C3CB892FC498FCE56A34F732FBFF32E79C7B9747D9094E8635A0C084D6F0247F9768FB5FF83493799A9BEC6C39572120C40E9292C8C947AE8573462A9108C36D9D7112E6995AE5867E6C8BB387D1C5D4BEF524F391B9FD9F0A3B4BFA079E915BCD920185CFD38D114C558928BD7D47877"}, |
||||
{"38A8E45D6D705A11AF58AED5A1344896998EACF359F2E26A", |
||||
"FD82B5B31804FF47D44199B533D0CF84", |
||||
"DE454D4E62FE879F2050EE3E25853623D3E9AC52EEC1A1779A48CFAF5ECA0BFDE44749391866D1", |
||||
"B804", |
||||
"164BB965C05EBE0931A1A63293EDF9C38C27"}, |
||||
{"34C33C97C6D7A0850DA94D78A58DC61EC717CD7574833068", |
||||
"343BE00DA9483F05C14F2E9EB8EA6AE8", |
||||
"78312A43EFDE3CAE34A65796FF059A3FE15304EEA5CF1D9306949FE5BF3349D4977D4EBE76C040FE894C5949E4E4D6681153DA87FB9AC5062063CA2EA183566343362370944CE0362D25FC195E124FD60E8682E665D13F2229DDA3E4B2CB1DCA", |
||||
"CC11BB284B1153578E4A5ED9D937B869DAF00F5B1960C23455CA9CC43F486A3BE0B66254F1041F04FDF459C8640465B6E1D2CF899A381451E8E7FCB50CF87823BE77E24B132BBEEDC72E53369B275E1D8F49ECE59F4F215230AC4FE133FC80E4F634EE80BA4682B62C86", |
||||
"E7F703DC31A95E3A4919FF957836CB76C063D81702AEA4703E1C2BF30831E58C4609D626EC6810E12EAA5B930F049FF9EFC22C3E3F1EBD4A1FB285CB02A1AC5AD46B425199FC0A85670A5C4E3DAA9636C8F64C199F42F18AAC8EA7457FD377F322DD7752D7D01B946C8F0A97E6113F0D50106F319AFD291AAACE"}, |
||||
{"C6ECF7F053573E403E61B83052A343D93CBCC179D1E835BE", |
||||
"E280E13D7367042E3AA09A80111B6184", |
||||
"21486C9D7A9647", |
||||
"5F2639AFA6F17931853791CD8C92382BBB677FD72D0AB1A080D0E49BFAA21810E963E4FACD422E92F65CBFAD5884A60CD94740DF31AF02F95AA57DA0C4401B0ED906", |
||||
"5C51DB20755302070C45F52E50128A67C8B2E4ED0EACB7E29998CCE2E8C289DD5655913EC1A51CC3AABE5CDC2402B2BE7D6D4BF6945F266FBD70BA9F37109067157AE7530678B45F64475D4EBFCB5FFF46A5"}, |
||||
{"5EC6CF7401BC57B18EF154E8C38ACCA8959E57D2F3975FF5", |
||||
"656B41CB3F9CF8C08BAD7EBFC80BD225", |
||||
"6B817C2906E2AF425861A7EF59BA5801F143EE2A139EE72697CDE168B4", |
||||
"2C0E1DDC9B1E5389BA63845B18B1F8A1DB062037151BCC56EF7C21C0BB4DAE366636BBA975685D7CC5A94AFBE89C769016388C56FB7B57CE750A12B718A8BDCF70E80E8659A8330EFC8F86640F21735E8C80E23FE43ABF23507CE3F964AE4EC99D", |
||||
"ED780CF911E6D1AA8C979B889B0B9DC1ABE261832980BDBFB576901D9EF5AB8048998E31A15BE54B3E5845A4D136AD24D0BDA1C3006168DF2F8AC06729CB0818867398150020131D8F04EDF1923758C9EABB5F735DE5EA1758D4BC0ACFCA98AFD202E9839B8720253693B874C65586C6F0"}, |
||||
{"C92F678EB2208662F5BCF3403EC05F5961E957908A3E79421E1D25FC19054153", |
||||
"DA0F3A40983D92F2D4C01FED33C7A192", |
||||
"2B6E9D26DB406A0FAB47608657AA10EFC2B4AA5F459B29FF85AC9A40BFFE7AEB04F77E9A11FAAA116D7F6D4DA417671A9AB02C588E0EF59CB1BFB4B1CC931B63A3B3A159FCEC97A04D1E6F0C7E6A9CEF6B0ABB04758A69F1FE754DF4C2610E8C46B6CF413BDB31351D55BEDCB7B4A13A1C98E10984475E0F2F957853", |
||||
"F37326A80E08", |
||||
"83519E53E321D334F7C10B568183775C0E9AAE55F806"}, |
||||
{"6847E0491BE57E72995D186D50094B0B3593957A5146798FCE68B287B2FB37B5", |
||||
"3EE1182AEBB19A02B128F28E1D5F7F99", |
||||
"D9F35ABB16D776CE", |
||||
"DB7566ED8EA95BDF837F23DB277BAFBC5E70D1105ADFD0D9EF15475051B1EF94709C67DCA9F8D5", |
||||
"2CDCED0C9EBD6E2A508822A685F7DCD1CDD99E7A5FCA786C234E7F7F1D27EC49751AD5DCFA30C5EDA87C43CAE3B919B6BBCFE34C8EDA59"}, |
||||
{"82B019673642C08388D3E42075A4D5D587558C229E4AB8F660E37650C4C41A0A", |
||||
"336F5D681E0410FAE7B607246092C6DC", |
||||
"D430CBD8FE435B64214E9E9CDC5DE99D31CFCFB8C10AA0587A49DF276611", |
||||
"998404153AD77003E1737EDE93ED79859EE6DCCA93CB40C4363AA817ABF2DBBD46E42A14A7183B6CC01E12A577888141363D0AE011EB6E8D28C0B235", |
||||
"9BEF69EEB60BD3D6065707B7557F25292A8872857CFBD24F2F3C088E4450995333088DA50FD9121221C504DF1D0CD5EFE6A12666C5D5BB12282CF4C19906E9CFAB97E9BDF7F49DC17CFC384B"}, |
||||
{"747B2E269B1859F0622C15C8BAD6A725028B1F94B8DB7326948D1E6ED663A8BC", |
||||
"AB91F7245DDCE3F1C747872D47BE0A8A", |
||||
"3B03F786EF1DDD76E1D42646DA4CD2A5165DC5383CE86D1A0B5F13F910DC278A4E451EE0192CBA178E13B3BA27FDC7840DF73D2E104B", |
||||
"6B803F4701114F3E5FE21718845F8416F70F626303F545BE197189E0A2BA396F37CE06D389EB2658BC7D56D67868708F6D0D32", |
||||
"1570DDB0BCE75AA25D1957A287A2C36B1A5F2270186DA81BA6112B7F43B0F3D1D0ED072591DCF1F1C99BBB25621FC39B896FF9BD9413A2845363A9DCD310C32CF98E57"}, |
||||
{"02E59853FB29AEDA0FE1C5F19180AD99A12FF2F144670BB2B8BADF09AD812E0A", |
||||
"C691294EF67CD04D1B9242AF83DD1421", |
||||
"879334DAE3", |
||||
"1E17F46A98FEF5CBB40759D95354", |
||||
"FED8C3FF27DDF6313AED444A2985B36CBA268AAD6AAC563C0BA28F6DB5DB"}, |
||||
{"F6C1FB9B4188F2288FF03BD716023198C3582CF2A037FC2F29760916C2B7FCDB", |
||||
"4228DA0678CA3534588859E77DFF014C", |
||||
"D8153CAF35539A61DD8D05B3C9B44F01E564FB9348BCD09A1C23B84195171308861058F0A3CD2A55B912A3AAEE06FF4D356C77275828F2157C2FC7C115DA39E443210CCC56BEDB0CC99BBFB227ABD5CC454F4E7F547C7378A659EEB6A7E809101A84F866503CB18D4484E1FA09B3EC7FC75EB2E35270800AA7", |
||||
"23B660A779AD285704B12EC1C580387A47BEC7B00D452C6570", |
||||
"5AA642BBABA8E49849002A2FAF31DB8FC7773EFDD656E469CEC19B3206D4174C9A263D0A05484261F6"}, |
||||
{"8FF6086F1FADB9A3FBE245EAC52640C43B39D43F89526BB5A6EBA47710931446", |
||||
"943188480C99437495958B0AE4831AA9", |
||||
"AD5CD0BDA426F6EBA23C8EB23DC73FF9FEC173355EDBD6C9344C4C4383F211888F7CE6B29899A6801DF6B38651A7C77150941A", |
||||
"80CD5EA8D7F81DDF5070B934937912E8F541A5301877528EB41AB60C020968D459960ED8FB73083329841A", |
||||
"ABAE8EB7F36FCA2362551E72DAC890BA1BB6794797E0FC3B67426EC9372726ED4725D379EA0AC9147E48DCD0005C502863C2C5358A38817C8264B5"}, |
||||
{"A083B54E6B1FE01B65D42FCD248F97BB477A41462BBFE6FD591006C022C8FD84", |
||||
"B0490F5BD68A52459556B3749ACDF40E", |
||||
"8892E047DA5CFBBDF7F3CFCBD1BD21C6D4C80774B1826999234394BD3E513CC7C222BB40E1E3140A152F19B3802F0D036C24A590512AD0E8", |
||||
"D7B15752789DC94ED0F36778A5C7BBB207BEC32BAC66E702B39966F06E381E090C6757653C3D26A81EC6AD6C364D66867A334C91BB0B8A8A4B6EACDF0783D09010AEBA2DD2062308FE99CC1F", |
||||
"C071280A732ADC93DF272BF1E613B2BB7D46FC6665EF2DC1671F3E211D6BDE1D6ADDD28DF3AA2E47053FC8BB8AE9271EC8BC8B2CFFA320D225B451685B6D23ACEFDD241FE284F8ADC8DB07F456985B14330BBB66E0FB212213E05B3E"}, |
||||
} |
@ -0,0 +1,92 @@ |
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
// This file contains necessary tools for the aex and ocb packages.
|
||||
//
|
||||
// These functions SHOULD NOT be used elsewhere, since they are optimized for
|
||||
// specific input nature in the EAX and OCB modes of operation.
|
||||
|
||||
package byteutil |
||||
|
||||
// GfnDouble computes 2 * input in the field of 2^n elements.
|
||||
// The irreducible polynomial in the finite field for n=128 is
|
||||
// x^128 + x^7 + x^2 + x + 1 (equals 0x87)
|
||||
// Constant-time execution in order to avoid side-channel attacks
|
||||
func GfnDouble(input []byte) []byte { |
||||
if len(input) != 16 { |
||||
panic("Doubling in GFn only implemented for n = 128") |
||||
} |
||||
// If the first bit is zero, return 2L = L << 1
|
||||
// Else return (L << 1) xor 0^120 10000111
|
||||
shifted := ShiftBytesLeft(input) |
||||
shifted[15] ^= ((input[0] >> 7) * 0x87) |
||||
return shifted |
||||
} |
||||
|
||||
// ShiftBytesLeft outputs the byte array corresponding to x << 1 in binary.
|
||||
func ShiftBytesLeft(x []byte) []byte { |
||||
l := len(x) |
||||
dst := make([]byte, l) |
||||
for i := 0; i < l-1; i++ { |
||||
dst[i] = (x[i] << 1) | (x[i+1] >> 7) |
||||
} |
||||
dst[l-1] = x[l-1] << 1 |
||||
return dst |
||||
} |
||||
|
||||
// ShiftNBytesLeft puts in dst the byte array corresponding to x << n in binary.
|
||||
func ShiftNBytesLeft(dst, x []byte, n int) { |
||||
// Erase first n / 8 bytes
|
||||
copy(dst, x[n/8:]) |
||||
|
||||
// Shift the remaining n % 8 bits
|
||||
bits := uint(n % 8) |
||||
l := len(dst) |
||||
for i := 0; i < l-1; i++ { |
||||
dst[i] = (dst[i] << bits) | (dst[i+1] >> uint(8 - bits)) |
||||
} |
||||
dst[l-1] = dst[l-1] << bits |
||||
|
||||
// Append trailing zeroes
|
||||
dst = append(dst, make([]byte, n/8)...) |
||||
} |
||||
|
||||
// XorBytesMut assumes equal input length, replaces X with X XOR Y
|
||||
func XorBytesMut(X, Y []byte) { |
||||
for i := 0; i < len(X); i++ { |
||||
X[i] ^= Y[i] |
||||
} |
||||
} |
||||
|
||||
|
||||
// XorBytes assumes equal input length, puts X XOR Y into Z
|
||||
func XorBytes(Z, X, Y []byte) { |
||||
for i := 0; i < len(X); i++ { |
||||
Z[i] = X[i] ^ Y[i] |
||||
} |
||||
} |
||||
|
||||
// RightXor XORs smaller input (assumed Y) at the right of the larger input (assumed X)
|
||||
func RightXor(X, Y []byte) []byte { |
||||
offset := len(X) - len(Y) |
||||
xored := make([]byte, len(X)); |
||||
copy(xored, X) |
||||
for i := 0; i < len(Y); i++ { |
||||
xored[offset + i] ^= Y[i] |
||||
} |
||||
return xored |
||||
} |
||||
|
||||
// SliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func SliceForAppend(in []byte, n int) (head, tail []byte) { |
||||
if total := len(in) + n; cap(in) >= total { |
||||
head = in[:total] |
||||
} else { |
||||
head = make([]byte, total) |
||||
copy(head, in) |
||||
} |
||||
tail = head[len(in):] |
||||
return |
||||
} |
||||
|
@ -0,0 +1,317 @@ |
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
// Package ocb provides an implementation of the OCB (offset codebook) mode of
|
||||
// operation, as described in RFC-7253 of the IRTF and in Rogaway, Bellare,
|
||||
// Black and Krovetz - OCB: A BLOCK-CIPHER MODE OF OPERATION FOR EFFICIENT
|
||||
// AUTHENTICATED ENCRYPTION (2003).
|
||||
// Security considerations (from RFC-7253): A private key MUST NOT be used to
|
||||
// encrypt more than 2^48 blocks. Tag length should be at least 12 bytes (a
|
||||
// brute-force forging adversary succeeds after 2^{tag length} attempts). A
|
||||
// single key SHOULD NOT be used to decrypt ciphertext with different tag
|
||||
// lengths. Nonces need not be secret, but MUST NOT be reused.
|
||||
// This package only supports underlying block ciphers with 128-bit blocks,
|
||||
// such as AES-{128, 192, 256}, but may be extended to other sizes.
|
||||
package ocb |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/cipher" |
||||
"crypto/subtle" |
||||
"errors" |
||||
"github.com/ProtonMail/go-crypto/internal/byteutil" |
||||
"math/bits" |
||||
) |
||||
|
||||
type ocb struct { |
||||
block cipher.Block |
||||
tagSize int |
||||
nonceSize int |
||||
mask mask |
||||
// Optimized en/decrypt: For each nonce N used to en/decrypt, the 'Ktop'
|
||||
// internal variable can be reused for en/decrypting with nonces sharing
|
||||
// all but the last 6 bits with N. The prefix of the first nonce used to
|
||||
// compute the new Ktop, and the Ktop value itself, are stored in
|
||||
// reusableKtop. If using incremental nonces, this saves one block cipher
|
||||
// call every 63 out of 64 OCB encryptions, and stores one nonce and one
|
||||
// output of the block cipher in memory only.
|
||||
reusableKtop reusableKtop |
||||
} |
||||
|
||||
type mask struct { |
||||
// L_*, L_$, (L_i)_{i ∈ N}
|
||||
lAst []byte |
||||
lDol []byte |
||||
L [][]byte |
||||
} |
||||
|
||||
type reusableKtop struct { |
||||
noncePrefix []byte |
||||
Ktop []byte |
||||
} |
||||
|
||||
const ( |
||||
defaultTagSize = 16 |
||||
defaultNonceSize = 15 |
||||
) |
||||
|
||||
const ( |
||||
enc = iota |
||||
dec |
||||
) |
||||
|
||||
func (o *ocb) NonceSize() int { |
||||
return o.nonceSize |
||||
} |
||||
|
||||
func (o *ocb) Overhead() int { |
||||
return o.tagSize |
||||
} |
||||
|
||||
// NewOCB returns an OCB instance with the given block cipher and default
|
||||
// tag and nonce sizes.
|
||||
func NewOCB(block cipher.Block) (cipher.AEAD, error) { |
||||
return NewOCBWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize) |
||||
} |
||||
|
||||
// NewOCBWithNonceAndTagSize returns an OCB instance with the given block
|
||||
// cipher, nonce length, and tag length. Panics on zero nonceSize and
|
||||
// exceedingly long tag size.
|
||||
//
|
||||
// It is recommended to use at least 12 bytes as tag length.
|
||||
func NewOCBWithNonceAndTagSize( |
||||
block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) { |
||||
if block.BlockSize() != 16 { |
||||
return nil, ocbError("Block cipher must have 128-bit blocks") |
||||
} |
||||
if nonceSize < 1 { |
||||
return nil, ocbError("Incorrect nonce length") |
||||
} |
||||
if nonceSize >= block.BlockSize() { |
||||
return nil, ocbError("Nonce length exceeds blocksize - 1") |
||||
} |
||||
if tagSize > block.BlockSize() { |
||||
return nil, ocbError("Custom tag length exceeds blocksize") |
||||
} |
||||
return &ocb{ |
||||
block: block, |
||||
tagSize: tagSize, |
||||
nonceSize: nonceSize, |
||||
mask: initializeMaskTable(block), |
||||
reusableKtop: reusableKtop{ |
||||
noncePrefix: nil, |
||||
Ktop: nil, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
func (o *ocb) Seal(dst, nonce, plaintext, adata []byte) []byte { |
||||
if len(nonce) > o.nonceSize { |
||||
panic("crypto/ocb: Incorrect nonce length given to OCB") |
||||
} |
||||
ret, out := byteutil.SliceForAppend(dst, len(plaintext)+o.tagSize) |
||||
o.crypt(enc, out, nonce, adata, plaintext) |
||||
return ret |
||||
} |
||||
|
||||
func (o *ocb) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) { |
||||
if len(nonce) > o.nonceSize { |
||||
panic("Nonce too long for this instance") |
||||
} |
||||
if len(ciphertext) < o.tagSize { |
||||
return nil, ocbError("Ciphertext shorter than tag length") |
||||
} |
||||
sep := len(ciphertext) - o.tagSize |
||||
ret, out := byteutil.SliceForAppend(dst, len(ciphertext)) |
||||
ciphertextData := ciphertext[:sep] |
||||
tag := ciphertext[sep:] |
||||
o.crypt(dec, out, nonce, adata, ciphertextData) |
||||
if subtle.ConstantTimeCompare(ret[sep:], tag) == 1 { |
||||
ret = ret[:sep] |
||||
return ret, nil |
||||
} |
||||
for i := range out { |
||||
out[i] = 0 |
||||
} |
||||
return nil, ocbError("Tag authentication failed") |
||||
} |
||||
|
||||
// On instruction enc (resp. dec), crypt is the encrypt (resp. decrypt)
|
||||
// function. It returns the resulting plain/ciphertext with the tag appended.
|
||||
func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte { |
||||
//
|
||||
// Consider X as a sequence of 128-bit blocks
|
||||
//
|
||||
// Note: For encryption (resp. decryption), X is the plaintext (resp., the
|
||||
// ciphertext without the tag).
|
||||
blockSize := o.block.BlockSize() |
||||
|
||||
//
|
||||
// Nonce-dependent and per-encryption variables
|
||||
//
|
||||
// Zero out the last 6 bits of the nonce into truncatedNonce to see if Ktop
|
||||
// is already computed.
|
||||
truncatedNonce := make([]byte, len(nonce)) |
||||
copy(truncatedNonce, nonce) |
||||
truncatedNonce[len(truncatedNonce)-1] &= 192 |
||||
Ktop := make([]byte, blockSize) |
||||
if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) { |
||||
Ktop = o.reusableKtop.Ktop |
||||
} else { |
||||
// Nonce = num2str(TAGLEN mod 128, 7) || zeros(120 - bitlen(N)) || 1 || N
|
||||
paddedNonce := append(make([]byte, blockSize-1-len(nonce)), 1) |
||||
paddedNonce = append(paddedNonce, truncatedNonce...) |
||||
paddedNonce[0] |= byte(((8 * o.tagSize) % (8 * blockSize)) << 1) |
||||
// Last 6 bits of paddedNonce are already zero. Encrypt into Ktop
|
||||
paddedNonce[blockSize-1] &= 192 |
||||
Ktop = paddedNonce |
||||
o.block.Encrypt(Ktop, Ktop) |
||||
o.reusableKtop.noncePrefix = truncatedNonce |
||||
o.reusableKtop.Ktop = Ktop |
||||
} |
||||
|
||||
// Stretch = Ktop || ((lower half of Ktop) XOR (lower half of Ktop << 8))
|
||||
xorHalves := make([]byte, blockSize/2) |
||||
byteutil.XorBytes(xorHalves, Ktop[:blockSize/2], Ktop[1:1+blockSize/2]) |
||||
stretch := append(Ktop, xorHalves...) |
||||
bottom := int(nonce[len(nonce)-1] & 63) |
||||
offset := make([]byte, len(stretch)) |
||||
byteutil.ShiftNBytesLeft(offset, stretch, bottom) |
||||
offset = offset[:blockSize] |
||||
|
||||
//
|
||||
// Process any whole blocks
|
||||
//
|
||||
// Note: For encryption Y is ciphertext || tag, for decryption Y is
|
||||
// plaintext || tag.
|
||||
checksum := make([]byte, blockSize) |
||||
m := len(X) / blockSize |
||||
for i := 0; i < m; i++ { |
||||
index := bits.TrailingZeros(uint(i + 1)) |
||||
if len(o.mask.L)-1 < index { |
||||
o.mask.extendTable(index) |
||||
} |
||||
byteutil.XorBytesMut(offset, o.mask.L[bits.TrailingZeros(uint(i+1))]) |
||||
blockX := X[i*blockSize : (i+1)*blockSize] |
||||
blockY := Y[i*blockSize : (i+1)*blockSize] |
||||
byteutil.XorBytes(blockY, blockX, offset) |
||||
switch instruction { |
||||
case enc: |
||||
o.block.Encrypt(blockY, blockY) |
||||
byteutil.XorBytesMut(blockY, offset) |
||||
byteutil.XorBytesMut(checksum, blockX) |
||||
case dec: |
||||
o.block.Decrypt(blockY, blockY) |
||||
byteutil.XorBytesMut(blockY, offset) |
||||
byteutil.XorBytesMut(checksum, blockY) |
||||
} |
||||
} |
||||
//
|
||||
// Process any final partial block and compute raw tag
|
||||
//
|
||||
tag := make([]byte, blockSize) |
||||
if len(X)%blockSize != 0 { |
||||
byteutil.XorBytesMut(offset, o.mask.lAst) |
||||
pad := make([]byte, blockSize) |
||||
o.block.Encrypt(pad, offset) |
||||
chunkX := X[blockSize*m:] |
||||
chunkY := Y[blockSize*m : len(X)] |
||||
byteutil.XorBytes(chunkY, chunkX, pad[:len(chunkX)]) |
||||
// P_* || bit(1) || zeroes(127) - len(P_*)
|
||||
switch instruction { |
||||
case enc: |
||||
paddedY := append(chunkX, byte(128)) |
||||
paddedY = append(paddedY, make([]byte, blockSize-len(chunkX)-1)...) |
||||
byteutil.XorBytesMut(checksum, paddedY) |
||||
case dec: |
||||
paddedX := append(chunkY, byte(128)) |
||||
paddedX = append(paddedX, make([]byte, blockSize-len(chunkY)-1)...) |
||||
byteutil.XorBytesMut(checksum, paddedX) |
||||
} |
||||
byteutil.XorBytes(tag, checksum, offset) |
||||
byteutil.XorBytesMut(tag, o.mask.lDol) |
||||
o.block.Encrypt(tag, tag) |
||||
byteutil.XorBytesMut(tag, o.hash(adata)) |
||||
copy(Y[blockSize*m+len(chunkY):], tag[:o.tagSize]) |
||||
} else { |
||||
byteutil.XorBytes(tag, checksum, offset) |
||||
byteutil.XorBytesMut(tag, o.mask.lDol) |
||||
o.block.Encrypt(tag, tag) |
||||
byteutil.XorBytesMut(tag, o.hash(adata)) |
||||
copy(Y[blockSize*m:], tag[:o.tagSize]) |
||||
} |
||||
return Y |
||||
} |
||||
|
||||
// This hash function is used to compute the tag. Per design, on empty input it
|
||||
// returns a slice of zeros, of the same length as the underlying block cipher
|
||||
// block size.
|
||||
func (o *ocb) hash(adata []byte) []byte { |
||||
//
|
||||
// Consider A as a sequence of 128-bit blocks
|
||||
//
|
||||
A := make([]byte, len(adata)) |
||||
copy(A, adata) |
||||
blockSize := o.block.BlockSize() |
||||
|
||||
//
|
||||
// Process any whole blocks
|
||||
//
|
||||
sum := make([]byte, blockSize) |
||||
offset := make([]byte, blockSize) |
||||
m := len(A) / blockSize |
||||
for i := 0; i < m; i++ { |
||||
chunk := A[blockSize*i : blockSize*(i+1)] |
||||
index := bits.TrailingZeros(uint(i + 1)) |
||||
// If the mask table is too short
|
||||
if len(o.mask.L)-1 < index { |
||||
o.mask.extendTable(index) |
||||
} |
||||
byteutil.XorBytesMut(offset, o.mask.L[index]) |
||||
byteutil.XorBytesMut(chunk, offset) |
||||
o.block.Encrypt(chunk, chunk) |
||||
byteutil.XorBytesMut(sum, chunk) |
||||
} |
||||
|
||||
//
|
||||
// Process any final partial block; compute final hash value
|
||||
//
|
||||
if len(A)%blockSize != 0 { |
||||
byteutil.XorBytesMut(offset, o.mask.lAst) |
||||
// Pad block with 1 || 0 ^ 127 - bitlength(a)
|
||||
ending := make([]byte, blockSize-len(A)%blockSize) |
||||
ending[0] = 0x80 |
||||
encrypted := append(A[blockSize*m:], ending...) |
||||
byteutil.XorBytesMut(encrypted, offset) |
||||
o.block.Encrypt(encrypted, encrypted) |
||||
byteutil.XorBytesMut(sum, encrypted) |
||||
} |
||||
return sum |
||||
} |
||||
|
||||
func initializeMaskTable(block cipher.Block) mask { |
||||
//
|
||||
// Key-dependent variables
|
||||
//
|
||||
lAst := make([]byte, block.BlockSize()) |
||||
block.Encrypt(lAst, lAst) |
||||
lDol := byteutil.GfnDouble(lAst) |
||||
L := make([][]byte, 1) |
||||
L[0] = byteutil.GfnDouble(lDol) |
||||
|
||||
return mask{ |
||||
lAst: lAst, |
||||
lDol: lDol, |
||||
L: L, |
||||
} |
||||
} |
||||
|
||||
// Extends the L array of mask m up to L[limit], with L[i] = GfnDouble(L[i-1])
|
||||
func (m *mask) extendTable(limit int) { |
||||
for i := len(m.L); i <= limit; i++ { |
||||
m.L = append(m.L, byteutil.GfnDouble(m.L[i-1])) |
||||
} |
||||
} |
||||
|
||||
func ocbError(err string) error { |
||||
return errors.New("crypto/ocb: " + err) |
||||
} |
@ -0,0 +1,136 @@ |
||||
// In the test vectors provided by RFC 7253, the "bottom"
|
||||
// internal variable, which defines "offset" for the first time, does not
|
||||
// exceed 15. However, it can attain values up to 63.
|
||||
|
||||
// These vectors include key length in {128, 192, 256}, tag size 128, and
|
||||
// random nonce, header, and plaintext lengths.
|
||||
|
||||
// This file was automatically generated.
|
||||
|
||||
package ocb |
||||
|
||||
var randomVectors = []struct { |
||||
key, nonce, header, plaintext, ciphertext string |
||||
}{ |
||||
|
||||
{"9438C5D599308EAF13F800D2D31EA7F0", |
||||
"C38EE4801BEBFFA1CD8635BE", |
||||
"0E507B7DADD8A98CDFE272D3CB6B3E8332B56AE583FB049C0874D4200BED16BD1A044182434E9DA0E841F182DFD5B3016B34641CED0784F1745F63AB3D0DA22D3351C9EF9A658B8081E24498EBF61FCE40DA6D8E184536", |
||||
"962D227786FB8913A8BAD5DC3250", |
||||
"EEDEF5FFA5986D1E3BF86DDD33EF9ADC79DCA06E215FA772CCBA814F63AD"}, |
||||
{"BA7DE631C7D6712167C6724F5B9A2B1D", |
||||
"35263EBDA05765DC0E71F1F5", |
||||
"0103257B4224507C0242FEFE821EA7FA42E0A82863E5F8B68F7D881B4B44FA428A2B6B21D2F591260802D8AB6D83", |
||||
"9D6D1FC93AE8A64E7889B7B2E3521EFA9B920A8DDB692E6F833DDC4A38AFA535E5E2A3ED82CB7E26404AB86C54D01C4668F28398C2DF33D5D561CBA1C8DCFA7A912F5048E545B59483C0E3221F54B14DAA2E4EB657B3BEF9554F34CAD69B2724AE962D3D8A", |
||||
"E93852D1985C5E775655E937FA79CE5BF28A585F2AF53A5018853B9634BE3C84499AC0081918FDCE0624494D60E25F76ACD6853AC7576E3C350F332249BFCABD4E73CEABC36BE4EDDA40914E598AE74174A0D7442149B26990899491BDDFE8FC54D6C18E83AE9E9A6FFBF5D376565633862EEAD88D"}, |
||||
{"2E74B25289F6FD3E578C24866E9C72A5", |
||||
"FD912F15025AF8414642BA1D1D", |
||||
"FB5FB8C26F365EEDAB5FE260C6E3CCD27806729C8335F146063A7F9EA93290E56CF84576EB446350D22AD730547C267B1F0BBB97EB34E1E2C41A", |
||||
"6C092EBF78F76EE8C1C6E592277D9545BA16EDB67BC7D8480B9827702DC2F8A129E2B08A2CE710CA7E1DA45CE162BB6CD4B512E632116E2211D3C90871EFB06B8D4B902681C7FB", |
||||
"6AC0A77F26531BF4F354A1737F99E49BE32ECD909A7A71AD69352906F54B08A9CE9B8CA5D724CBFFC5673437F23F630697F3B84117A1431D6FA8CC13A974FB4AD360300522E09511B99E71065D5AC4BBCB1D791E864EF4"}, |
||||
{"E7EC507C802528F790AFF5303A017B17", |
||||
"4B97A7A568940A9E3CE7A99E93031E", |
||||
"28349BDC5A09390C480F9B8AA3EDEA3DDB8B9D64BCA322C570B8225DF0E31190DAB25A4014BA39519E02ABFB12B89AA28BBFD29E486E7FB28734258C817B63CED9912DBAFEBB93E2798AB2890DE3B0ACFCFF906AB15563EF7823CE83D27CDB251195E22BD1337BCBDE65E7C2C427321C463C2777BFE5AEAA", |
||||
"9455B3EA706B74", |
||||
"7F33BA3EA848D48A96B9530E26888F43EBD4463C9399B6"}, |
||||
{"6C928AA3224736F28EE7378DE0090191", |
||||
"8936138E2E4C6A13280017A1622D", |
||||
"6202717F2631565BDCDC57C6584543E72A7C8BD444D0D108ED35069819633C", |
||||
"DA0691439E5F035F3E455269D14FE5C201C8C9B0A3FE2D3F86BCC59387C868FE65733D388360B31E3CE28B4BF6A8BE636706B536D5720DB66B47CF1C7A5AFD6F61E0EF90F1726D6B0E169F9A768B2B7AE4EE00A17F630AC905FCAAA1B707FFF25B3A1AAE83B504837C64A5639B2A34002B300EC035C9B43654DA55", |
||||
"B8804D182AB0F0EEB464FA7BD1329AD6154F982013F3765FEDFE09E26DAC078C9C1439BFC1159D6C02A25E3FF83EF852570117B315852AD5EE20E0FA3AA0A626B0E43BC0CEA38B44579DD36803455FB46989B90E6D229F513FD727AF8372517E9488384C515D6067704119C931299A0982EDDFB9C2E86A90C450C077EB222511EC9CCABC9FCFDB19F70088"}, |
||||
{"ECEA315CA4B3F425B0C9957A17805EA4", |
||||
"664CDAE18403F4F9BA13015A44FC", |
||||
"642AFB090D6C6DB46783F08B01A3EF2A8FEB5736B531EAC226E7888FCC8505F396818F83105065FACB3267485B9E5E4A0261F621041C08FCCB2A809A49AB5252A91D0971BCC620B9D614BD77E57A0EED2FA5", |
||||
"6852C31F8083E20E364CEA21BB7854D67CEE812FE1C9ED2425C0932A90D3780728D1BB", |
||||
"2ECEF962A9695A463ADABB275BDA9FF8B2BA57AEC2F52EFFB700CD9271A74D2A011C24AEA946051BD6291776429B7E681BA33E"}, |
||||
{"4EE616C4A58AAA380878F71A373461F6", |
||||
"91B8C9C176D9C385E9C47E52", |
||||
"CDA440B7F9762C572A718AC754EDEECC119E5EE0CCB9FEA4FFB22EEE75087C032EBF3DA9CDD8A28CC010B99ED45143B41A4BA50EA2A005473F89639237838867A57F23B0F0ED3BF22490E4501DAC9C658A9B9F", |
||||
"D6E645FA9AE410D15B8123FD757FA356A8DBE9258DDB5BE88832E615910993F497EC", |
||||
"B70ED7BF959FB2AAED4F36174A2A99BFB16992C8CDF369C782C4DB9C73DE78C5DB8E0615F647243B97ACDB24503BC9CADC48"}, |
||||
{"DCD475773136C830D5E3D0C5FE05B7FF", |
||||
"BB8E1FBB483BE7616A922C4A", |
||||
"36FEF2E1CB29E76A6EA663FC3AF66ECD7404F466382F7B040AABED62293302B56E8783EF7EBC21B4A16C3E78A7483A0A403F253A2CDC5BBF79DC3DAE6C73F39A961D8FBBE8D41B", |
||||
"441E886EA38322B2437ECA7DEB5282518865A66780A454E510878E61BFEC3106A3CD93D2A02052E6F9E1832F9791053E3B76BF4C07EFDD6D4106E3027FABB752E60C1AA425416A87D53938163817A1051EBA1D1DEEB4B9B25C7E97368B52E5911A31810B0EC5AF547559B6142D9F4C4A6EF24A4CF75271BF9D48F62B", |
||||
"1BE4DD2F4E25A6512C2CC71D24BBB07368589A94C2714962CD0ACE5605688F06342587521E75F0ACAFFD86212FB5C34327D238DB36CF2B787794B9A4412E7CD1410EA5DDD2450C265F29CF96013CD213FD2880657694D718558964BC189B4A84AFCF47EB012935483052399DBA5B088B0A0477F20DFE0E85DCB735E21F22A439FB837DD365A93116D063E607"}, |
||||
{"3FBA2B3D30177FFE15C1C59ED2148BB2C091F5615FBA7C07", |
||||
"FACF804A4BEBF998505FF9DE", |
||||
"8213B9263B2971A5BDA18DBD02208EE1", |
||||
"15B323926993B326EA19F892D704439FC478828322AF72118748284A1FD8A6D814E641F70512FD706980337379F31DC63355974738D7FEA87AD2858C0C2EBBFBE74371C21450072373C7B651B334D7C4D43260B9D7CCD3AF9EDB", |
||||
"6D35DC1469B26E6AAB26272A41B46916397C24C485B61162E640A062D9275BC33DDCFD3D9E1A53B6C8F51AC89B66A41D59B3574197A40D9B6DCF8A4E2A001409C8112F16B9C389E0096179DB914E05D6D11ED0005AD17E1CE105A2F0BAB8F6B1540DEB968B7A5428FF44"}, |
||||
{"53B52B8D4D748BCDF1DDE68857832FA46227FA6E2F32EFA1", |
||||
"0B0EF53D4606B28D1398355F", |
||||
"F23882436349094AF98BCACA8218E81581A043B19009E28EFBF2DE37883E04864148CC01D240552CA8844EC1456F42034653067DA67E80F87105FD06E14FF771246C9612867BE4D215F6D761", |
||||
"F15030679BD4088D42CAC9BF2E9606EAD4798782FA3ED8C57EBE7F84A53236F51B25967C6489D0CD20C9EEA752F9BC", |
||||
"67B96E2D67C3729C96DAEAEDF821D61C17E648643A2134C5621FEC621186915AD80864BFD1EB5B238BF526A679385E012A457F583AFA78134242E9D9C1B4E4"}, |
||||
{"0272DD80F23399F49BFC320381A5CD8225867245A49A7D41", |
||||
"5C83F4896D0738E1366B1836", |
||||
"69B0337289B19F73A12BAEEA857CCAF396C11113715D9500CCCF48BA08CFF12BC8B4BADB3084E63B85719DB5058FA7C2C11DEB096D7943CFA7CAF5", |
||||
"C01AD10FC8B562CD17C7BC2FAB3E26CBDFF8D7F4DEA816794BBCC12336991712972F52816AABAB244EB43B0137E2BAC1DD413CE79531E78BEF782E6B439612BB3AEF154DE3502784F287958EBC159419F9EBA27916A28D6307324129F506B1DE80C1755A929F87", |
||||
"FEFE52DD7159C8DD6E8EC2D3D3C0F37AB6CB471A75A071D17EC4ACDD8F3AA4D7D4F7BB559F3C09099E3D9003E5E8AA1F556B79CECDE66F85B08FA5955E6976BF2695EA076388A62D2AD5BAB7CBF1A7F3F4C8D5CDF37CDE99BD3E30B685D9E5EEE48C7C89118EF4878EB89747F28271FA2CC45F8E9E7601"}, |
||||
{"3EEAED04A455D6E5E5AB53CFD5AFD2F2BC625C7BF4BE49A5", |
||||
"36B88F63ADBB5668588181D774", |
||||
"D367E3CB3703E762D23C6533188EF7028EFF9D935A3977150361997EC9DEAF1E4794BDE26AA8B53C124980B1362EC86FCDDFC7A90073171C1BAEE351A53234B86C66E8AB92FAE99EC6967A6D3428892D80", |
||||
"573454C719A9A55E04437BF7CBAAF27563CCCD92ADD5E515CD63305DFF0687E5EEF790C5DCA5C0033E9AB129505E2775438D92B38F08F3B0356BA142C6F694", |
||||
"E9F79A5B432D9E682C9AAA5661CFC2E49A0FCB81A431E54B42EB73DD3BED3F377FEC556ABA81624BA64A5D739AD41467460088F8D4F442180A9382CA635745473794C382FCDDC49BA4EB6D8A44AE3C"}, |
||||
{"B695C691538F8CBD60F039D0E28894E3693CC7C36D92D79D", |
||||
"BC099AEB637361BAC536B57618", |
||||
"BFFF1A65AE38D1DC142C71637319F5F6508E2CB33C9DCB94202B359ED5A5ED8042E7F4F09231D32A7242976677E6F4C549BF65FADC99E5AF43F7A46FD95E16C2", |
||||
"081DF3FD85B415D803F0BE5AC58CFF0023FDDED99788296C3731D8", |
||||
"E50C64E3614D94FE69C47092E46ACC9957C6FEA2CCBF96BC62FBABE7424753C75F9C147C42AE26FE171531"}, |
||||
{"C9ACBD2718F0689A1BE9802A551B6B8D9CF5614DAF5E65ED", |
||||
"B1B0AAF373B8B026EB80422051D8", |
||||
"6648C0E61AC733C76119D23FB24548D637751387AA2EAE9D80E912B7BD486CAAD9EAF4D7A5FE2B54AAD481E8EC94BB4D558000896E2010462B70C9FED1E7273080D1", |
||||
"189F591F6CB6D59AFEDD14C341741A8F1037DC0DF00FC57CE65C30F49E860255CEA5DC6019380CC0FE8880BC1A9E685F41C239C38F36E3F2A1388865C5C311059C0A", |
||||
"922A5E949B61D03BE34AB5F4E58607D4504EA14017BB363DAE3C873059EA7A1C77A746FB78981671D26C2CF6D9F24952D510044CE02A10177E9DB42D0145211DFE6E84369C5E3BC2669EAB4147B2822895F9"}, |
||||
{"7A832BD2CF5BF4919F353CE2A8C86A5E406DA2D52BE16A72", |
||||
"2F2F17CECF7E5A756D10785A3CB9DB", |
||||
"61DA05E3788CC2D8405DBA70C7A28E5AF699863C9F72E6C6770126929F5D6FA267F005EBCF49495CB46400958A3AE80D1289D1C671", |
||||
"44E91121195A41AF14E8CFDBD39A4B517BE0DF1A72977ED8A3EEF8EEDA1166B2EB6DB2C4AE2E74FA0F0C74537F659BFBD141E5DDEC67E64EDA85AABD3F52C85A785B9FB3CECD70E7DF", |
||||
"BEDF596EA21288D2B84901E188F6EE1468B14D5161D3802DBFE00D60203A24E2AB62714BF272A45551489838C3A7FEAADC177B591836E73684867CCF4E12901DCF2064058726BBA554E84ADC5136F507E961188D4AF06943D3"}, |
||||
{"1508E8AE9079AA15F1CEC4F776B4D11BCCB061B58AA56C18", |
||||
"BCA625674F41D1E3AB47672DC0C3", |
||||
"8B12CF84F16360F0EAD2A41BC021530FFCEC7F3579CAE658E10E2D3D81870F65AFCED0C77C6C4C6E6BA424FF23088C796BA6195ABA35094BF1829E089662E7A95FC90750AE16D0C8AFA55DAC789D7735B970B58D4BE7CEC7341DA82A0179A01929C27A59C5063215B859EA43", |
||||
"E525422519ECE070E82C", |
||||
"B47BC07C3ED1C0A43BA52C43CBACBCDBB29CAF1001E09FDF7107"}, |
||||
{"7550C2761644E911FE9ADD119BAC07376BEA442845FEAD876D7E7AC1B713E464", |
||||
"36D2EC25ADD33CDEDF495205BBC923", |
||||
"7FCFE81A3790DE97FFC3DE160C470847EA7E841177C2F759571CBD837EA004A6CA8C6F4AEBFF2E9FD552D73EB8A30705D58D70C0B67AEEA280CBBF0A477358ACEF1E7508F2735CD9A0E4F9AC92B8C008F575D3B6278F1C18BD01227E3502E5255F3AB1893632AD00C717C588EF652A51A43209E7EE90", |
||||
"2B1A62F8FDFAA3C16470A21AD307C9A7D03ADE8EF72C69B06F8D738CDE578D7AEFD0D40BD9C022FB9F580DF5394C998ACCCEFC5471A3996FB8F1045A81FDC6F32D13502EA65A211390C8D882B8E0BEFD8DD8CBEF51D1597B124E9F7F", |
||||
"C873E02A22DB89EB0787DB6A60B99F7E4A0A085D5C4232A81ADCE2D60AA36F92DDC33F93DD8640AC0E08416B187FB382B3EC3EE85A64B0E6EE41C1366A5AD2A282F66605E87031CCBA2FA7B2DA201D975994AADE3DD1EE122AE09604AD489B84BF0C1AB7129EE16C6934850E"}, |
||||
{"A51300285E554FDBDE7F771A9A9A80955639DD87129FAEF74987C91FB9687C71", |
||||
"81691D5D20EC818FCFF24B33DECC", |
||||
"C948093218AA9EB2A8E44A87EEA73FC8B6B75A196819A14BD83709EA323E8DF8B491045220E1D88729A38DBCFFB60D3056DAD4564498FD6574F74512945DEB34B69329ACED9FFC05D5D59DFCD5B973E2ACAFE6AD1EF8BBBC49351A2DD12508ED89ED", |
||||
"EB861165DAF7625F827C6B574ED703F03215", |
||||
"C6CD1CE76D2B3679C1B5AA1CFD67CCB55444B6BFD3E22C81CBC9BB738796B83E54E3"}, |
||||
{"8CE0156D26FAEB7E0B9B800BBB2E9D4075B5EAC5C62358B0E7F6FCE610223282", |
||||
"D2A7B94DD12CDACA909D3AD7", |
||||
"E021A78F374FC271389AB9A3E97077D755", |
||||
"7C26000B58929F5095E1CEE154F76C2A299248E299F9B5ADE6C403AA1FD4A67FD4E0232F214CE7B919EE7A1027D2B76C57475715CD078461", |
||||
"C556FB38DF069B56F337B5FF5775CE6EAA16824DFA754F20B78819028EA635C3BB7AA731DE8776B2DCB67DCA2D33EEDF3C7E52EA450013722A41755A0752433ED17BDD5991AAE77A"}, |
||||
{"1E8000A2CE00A561C9920A30BF0D7B983FEF8A1014C8F04C35CA6970E6BA02BD", |
||||
"65ED3D63F79F90BBFD19775E", |
||||
"336A8C0B7243582A46B221AA677647FCAE91", |
||||
"134A8B34824A290E7B", |
||||
"914FBEF80D0E6E17F8BDBB6097EBF5FBB0554952DC2B9E5151"}, |
||||
{"53D5607BBE690B6E8D8F6D97F3DF2BA853B682597A214B8AA0EA6E598650AF15", |
||||
"C391A856B9FE234E14BA1AC7BB40FF", |
||||
"479682BC21349C4BE1641D5E78FE2C79EC1B9CF5470936DCAD9967A4DCD7C4EFADA593BC9EDE71E6A08829B8580901B61E274227E9D918502DE3", |
||||
"EAD154DC09C5E26C5D26FF33ED148B27120C7F2C23225CC0D0631B03E1F6C6D96FEB88C1A4052ACB4CE746B884B6502931F407021126C6AAB8C514C077A5A38438AE88EE", |
||||
"938821286EBB671D999B87C032E1D6055392EB564E57970D55E545FC5E8BAB90E6E3E3C0913F6320995FC636D72CD9919657CC38BD51552F4A502D8D1FE56DB33EBAC5092630E69EBB986F0E15CEE9FC8C052501"}, |
||||
{"294362FCC984F440CEA3E9F7D2C06AF20C53AAC1B3738CA2186C914A6E193ABB", |
||||
"B15B61C8BB39261A8F55AB178EC3", |
||||
"D0729B6B75BB", |
||||
"2BD089ADCE9F334BAE3B065996C7D616DD0C27DF4218DCEEA0FBCA0F968837CE26B0876083327E25681FDDD620A32EC0DA12F73FAE826CC94BFF2B90A54D2651", |
||||
"AC94B25E4E21DE2437B806966CCD5D9385EF0CD4A51AB9FA6DE675C7B8952D67802E9FEC1FDE9F5D1EAB06057498BC0EEA454804FC9D2068982A3E24182D9AC2E7AB9994DDC899A604264583F63D066B"}, |
||||
{"959DBFEB039B1A5B8CE6A44649B602AAA5F98A906DB96143D202CD2024F749D9", |
||||
"01D7BDB1133E9C347486C1EFA6", |
||||
"F3843955BD741F379DD750585EDC55E2CDA05CCBA8C1F4622AC2FE35214BC3A019B8BD12C4CC42D9213D1E1556941E8D8450830287FFB3B763A13722DD4140ED9846FB5FFF745D7B0B967D810A068222E10B259AF1D392035B0D83DC1498A6830B11B2418A840212599171E0258A1C203B05362978", |
||||
"A21811232C950FA8B12237C2EBD6A7CD2C3A155905E9E0C7C120", |
||||
"63C1CE397B22F1A03F1FA549B43178BC405B152D3C95E977426D519B3DFCA28498823240592B6EEE7A14"}, |
||||
{"096AE499F5294173F34FF2B375F0E5D5AB79D0D03B33B1A74D7D576826345DF4", |
||||
"0C52B3D11D636E5910A4DD76D32C", |
||||
"229E9ECA3053789E937447BC719467075B6138A142DA528DA8F0CF8DDF022FD9AF8E74779BA3AC306609", |
||||
"8B7A00038783E8BAF6EDEAE0C4EAB48FC8FD501A588C7E4A4DB71E3604F2155A97687D3D2FFF8569261375A513CF4398CE0F87CA1658A1050F6EF6C4EA3E25", |
||||
"C20B6CF8D3C8241825FD90B2EDAC7593600646E579A8D8DAAE9E2E40C3835FE801B2BE4379131452BC5182C90307B176DFBE2049544222FE7783147B690774F6D9D7CEF52A91E61E298E9AA15464AC"}, |
||||
} |
@ -0,0 +1,78 @@ |
||||
package ocb |
||||
|
||||
import ( |
||||
"encoding/hex" |
||||
) |
||||
|
||||
// Test vectors from https://tools.ietf.org/html/rfc7253. Note that key is
|
||||
// shared accross tests.
|
||||
var testKey, _ = hex.DecodeString("000102030405060708090A0B0C0D0E0F") |
||||
|
||||
var rfc7253testVectors = []struct { |
||||
nonce, header, plaintext, ciphertext string |
||||
}{ |
||||
{"BBAA99887766554433221100", |
||||
"", |
||||
"", |
||||
"785407BFFFC8AD9EDCC5520AC9111EE6"}, |
||||
{"BBAA99887766554433221101", |
||||
"0001020304050607", |
||||
"0001020304050607", |
||||
"6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"}, |
||||
{"BBAA99887766554433221102", |
||||
"0001020304050607", |
||||
"", |
||||
"81017F8203F081277152FADE694A0A00"}, |
||||
{"BBAA99887766554433221103", |
||||
"", |
||||
"0001020304050607", |
||||
"45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"}, |
||||
{"BBAA99887766554433221104", |
||||
"000102030405060708090A0B0C0D0E0F", |
||||
"000102030405060708090A0B0C0D0E0F", |
||||
"571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358"}, |
||||
{"BBAA99887766554433221105", |
||||
"000102030405060708090A0B0C0D0E0F", |
||||
"", |
||||
"8CF761B6902EF764462AD86498CA6B97"}, |
||||
{"BBAA99887766554433221106", |
||||
"", |
||||
"000102030405060708090A0B0C0D0E0F", |
||||
"5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D"}, |
||||
{"BBAA99887766554433221107", |
||||
"000102030405060708090A0B0C0D0E0F1011121314151617", |
||||
"000102030405060708090A0B0C0D0E0F1011121314151617", |
||||
"1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F"}, |
||||
{"BBAA99887766554433221108", |
||||
"000102030405060708090A0B0C0D0E0F1011121314151617", |
||||
"", |
||||
"6DC225A071FC1B9F7C69F93B0F1E10DE"}, |
||||
{"BBAA99887766554433221109", |
||||
"", |
||||
"000102030405060708090A0B0C0D0E0F1011121314151617", |
||||
"221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF"}, |
||||
{"BBAA9988776655443322110A", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", |
||||
"BD6F6C496201C69296C11EFD138A467ABD3C707924B964DEAFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"}, |
||||
{"BBAA9988776655443322110B", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", |
||||
"", |
||||
"FE80690BEE8A485D11F32965BC9D2A32"}, |
||||
{"BBAA9988776655443322110C", |
||||
"", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", |
||||
"2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF46040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"}, |
||||
{"BBAA9988776655443322110D", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", |
||||
"D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60"}, |
||||
{"BBAA9988776655443322110E", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", |
||||
"", |
||||
"C5CD9D1850C141E358649994EE701B68"}, |
||||
{"BBAA9988776655443322110F", |
||||
"", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", |
||||
"4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479"}, |
||||
} |
@ -0,0 +1,24 @@ |
||||
package ocb |
||||
|
||||
// Second set of test vectors from https://tools.ietf.org/html/rfc7253
|
||||
var rfc7253TestVectorTaglen96 = struct { |
||||
key, nonce, header, plaintext, ciphertext string |
||||
}{"0F0E0D0C0B0A09080706050403020100", |
||||
"BBAA9988776655443322110D", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", |
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", |
||||
"1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA"} |
||||
|
||||
var rfc7253AlgorithmTest = []struct { |
||||
KEYLEN, TAGLEN int |
||||
OUTPUT string }{ |
||||
{128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"}, |
||||
{192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"}, |
||||
{256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"}, |
||||
{128, 96, "77A3D8E73589158D25D01209"}, |
||||
{192, 96, "05D56EAD2752C86BE6932C5E"}, |
||||
{256, 96, "5458359AC23B0CBA9E6330DD"}, |
||||
{128, 64, "192C9B7BD90BA06A"}, |
||||
{192, 64, "0066BC6E0EF34E24"}, |
||||
{256, 64, "7D4EA5D445501CBE"}, |
||||
} |
@ -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 |
||||
} |
@ -0,0 +1,224 @@ |
||||
// 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" |
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"io" |
||||
) |
||||
|
||||
// A Block represents an OpenPGP armored structure.
|
||||
//
|
||||
// The encoded form is:
|
||||
// -----BEGIN Type-----
|
||||
// Headers
|
||||
//
|
||||
// base64-encoded Bytes
|
||||
// '=' base64 encoded checksum
|
||||
// -----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") |
||||
|
||||
const crc24Init = 0xb704ce |
||||
const crc24Poly = 0x1864cfb |
||||
const crc24Mask = 0xffffff |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
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 and records the expected CRC value.
|
||||
type lineReader struct { |
||||
in *bufio.Reader |
||||
buf []byte |
||||
eof bool |
||||
crc uint32 |
||||
crcSet 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
|
||||
var expectedBytes [3]byte |
||||
var m int |
||||
m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) |
||||
if m != 3 || err != nil { |
||||
return |
||||
} |
||||
l.crc = uint32(expectedBytes[0])<<16 | |
||||
uint32(expectedBytes[1])<<8 | |
||||
uint32(expectedBytes[2]) |
||||
|
||||
line, _, err = l.in.ReadLine() |
||||
if err != nil && err != io.EOF { |
||||
return |
||||
} |
||||
if !bytes.HasPrefix(line, armorEnd) { |
||||
return 0, ArmorCorrupt |
||||
} |
||||
|
||||
l.eof = true |
||||
l.crcSet = 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, but keeps
|
||||
// a running CRC of the resulting data and checks the CRC against the value
|
||||
// found by the lineReader at EOF.
|
||||
type openpgpReader struct { |
||||
lReader *lineReader |
||||
b64Reader io.Reader |
||||
currentCRC uint32 |
||||
} |
||||
|
||||
func (r *openpgpReader) Read(p []byte) (n int, err error) { |
||||
n, err = r.b64Reader.Read(p) |
||||
r.currentCRC = crc24(r.currentCRC, p[:n]) |
||||
|
||||
if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { |
||||
return 0, ArmorCorrupt |
||||
} |
||||
|
||||
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]) |
||||
p.Header[lastKey] = string(line[i+2:]) |
||||
} |
||||
|
||||
p.lReader.in = r |
||||
p.oReader.currentCRC = crc24Init |
||||
p.oReader.lReader = &p.lReader |
||||
p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) |
||||
p.Body = &p.oReader |
||||
|
||||
return |
||||
} |
@ -0,0 +1,160 @@ |
||||
// 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" |
||||
) |
||||
|
||||
var armorHeaderSep = []byte(": ") |
||||
var blockEnd = []byte("\n=") |
||||
var newline = []byte("\n") |
||||
var armorEndOfLineOut = []byte("-----\n") |
||||
|
||||
// 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 |
||||
blockType []byte |
||||
} |
||||
|
||||
func (e *encoding) Write(data []byte) (n int, err error) { |
||||
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() |
||||
|
||||
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) |
||||
} |
||||
|
||||
// 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) { |
||||
bType := []byte(blockType) |
||||
err = writeSlices(out, armorStart, bType, armorEndOfLineOut) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
for k, v := range headers { |
||||
err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
_, err = out.Write(newline) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
e := &encoding{ |
||||
out: out, |
||||
breaker: newLineBreaker(out, 64), |
||||
crc: crc24Init, |
||||
blockType: bType, |
||||
} |
||||
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) |
||||
return e, nil |
||||
} |
@ -0,0 +1,65 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package openpgp |
||||
|
||||
import ( |
||||
"hash" |
||||
"io" |
||||
) |
||||
|
||||
// NewCanonicalTextHash reformats text written to it into the canonical
|
||||
// form and then applies the hash h. See RFC 4880, section 5.2.1.
|
||||
func NewCanonicalTextHash(h hash.Hash) hash.Hash { |
||||
return &canonicalTextHash{h, 0} |
||||
} |
||||
|
||||
type canonicalTextHash struct { |
||||
h hash.Hash |
||||
s int |
||||
} |
||||
|
||||
var newline = []byte{'\r', '\n'} |
||||
|
||||
func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) { |
||||
start := 0 |
||||
for i, c := range buf { |
||||
switch *s { |
||||
case 0: |
||||
if c == '\r' { |
||||
*s = 1 |
||||
} else if c == '\n' { |
||||
cw.Write(buf[start:i]) |
||||
cw.Write(newline) |
||||
start = i + 1 |
||||
} |
||||
case 1: |
||||
*s = 0 |
||||
} |
||||
} |
||||
|
||||
cw.Write(buf[start:]) |
||||
return len(buf), nil |
||||
} |
||||
|
||||
func (cth *canonicalTextHash) Write(buf []byte) (int, error) { |
||||
return writeCanonical(cth.h, buf, &cth.s) |
||||
} |
||||
|
||||
func (cth *canonicalTextHash) Sum(in []byte) []byte { |
||||
return cth.h.Sum(in) |
||||
} |
||||
|
||||
func (cth *canonicalTextHash) Reset() { |
||||
cth.h.Reset() |
||||
cth.s = 0 |
||||
} |
||||
|
||||
func (cth *canonicalTextHash) Size() int { |
||||
return cth.h.Size() |
||||
} |
||||
|
||||
func (cth *canonicalTextHash) BlockSize() int { |
||||
return cth.h.BlockSize() |
||||
} |
@ -0,0 +1,165 @@ |
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ecdh implements ECDH encryption, suitable for OpenPGP,
|
||||
// as specified in RFC 6637, section 8.
|
||||
package ecdh |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/elliptic" |
||||
"errors" |
||||
"io" |
||||
"math/big" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc" |
||||
) |
||||
|
||||
type KDF struct { |
||||
Hash algorithm.Hash |
||||
Cipher algorithm.Cipher |
||||
} |
||||
|
||||
type PublicKey struct { |
||||
ecc.CurveType |
||||
elliptic.Curve |
||||
X, Y *big.Int |
||||
KDF |
||||
} |
||||
|
||||
type PrivateKey struct { |
||||
PublicKey |
||||
D []byte |
||||
} |
||||
|
||||
func GenerateKey(c elliptic.Curve, kdf KDF, rand io.Reader) (priv *PrivateKey, err error) { |
||||
priv = new(PrivateKey) |
||||
priv.PublicKey.Curve = c |
||||
priv.PublicKey.KDF = kdf |
||||
priv.D, priv.PublicKey.X, priv.PublicKey.Y, err = elliptic.GenerateKey(c, rand) |
||||
return |
||||
} |
||||
|
||||
func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) { |
||||
if len(msg) > 40 { |
||||
return nil, nil, errors.New("ecdh: message too long") |
||||
} |
||||
// the sender MAY use 21, 13, and 5 bytes of padding for AES-128,
|
||||
// AES-192, and AES-256, respectively, to provide the same number of
|
||||
// octets, 40 total, as an input to the key wrapping method.
|
||||
padding := make([]byte, 40-len(msg)) |
||||
for i := range padding { |
||||
padding[i] = byte(40 - len(msg)) |
||||
} |
||||
m := append(msg, padding...) |
||||
|
||||
if pub.CurveType == ecc.Curve25519 { |
||||
return X25519Encrypt(random, pub, m, curveOID, fingerprint) |
||||
} |
||||
|
||||
d, x, y, err := elliptic.GenerateKey(pub.Curve, random) |
||||
if err != nil { |
||||
return nil, nil, err |
||||
} |
||||
|
||||
vsG = elliptic.Marshal(pub.Curve, x, y) |
||||
zbBig, _ := pub.Curve.ScalarMult(pub.X, pub.Y, d) |
||||
|
||||
byteLen := (pub.Curve.Params().BitSize + 7) >> 3 |
||||
zb := make([]byte, byteLen) |
||||
zbBytes := zbBig.Bytes() |
||||
copy(zb[byteLen-len(zbBytes):], zbBytes) |
||||
|
||||
z, err := buildKey(pub, zb, curveOID, fingerprint, false, false) |
||||
if err != nil { |
||||
return nil, nil, err |
||||
} |
||||
|
||||
if c, err = keywrap.Wrap(z, m); err != nil { |
||||
return nil, nil, err |
||||
} |
||||
|
||||
return vsG, c, nil |
||||
|
||||
} |
||||
|
||||
func Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) { |
||||
if priv.PublicKey.CurveType == ecc.Curve25519 { |
||||
return X25519Decrypt(priv, vsG, m, curveOID, fingerprint) |
||||
} |
||||
x, y := elliptic.Unmarshal(priv.Curve, vsG) |
||||
zbBig, _ := priv.Curve.ScalarMult(x, y, priv.D) |
||||
|
||||
byteLen := (priv.Curve.Params().BitSize + 7) >> 3 |
||||
zb := make([]byte, byteLen) |
||||
zbBytes := zbBig.Bytes() |
||||
copy(zb[byteLen-len(zbBytes):], zbBytes) |
||||
|
||||
z, err := buildKey(&priv.PublicKey, zb, curveOID, fingerprint, false, false) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
c, err := keywrap.Unwrap(z, m) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return c[:len(c)-int(c[len(c)-1])], nil |
||||
} |
||||
|
||||
func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLeading, stripTrailing bool) ([]byte, error) { |
||||
// Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
|
||||
// || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap
|
||||
// || "Anonymous Sender " || recipient_fingerprint;
|
||||
param := new(bytes.Buffer) |
||||
if _, err := param.Write(curveOID); err != nil { |
||||
return nil, err |
||||
} |
||||
algKDF := []byte{18, 3, 1, pub.KDF.Hash.Id(), pub.KDF.Cipher.Id()} |
||||
if _, err := param.Write(algKDF); err != nil { |
||||
return nil, err |
||||
} |
||||
if _, err := param.Write([]byte("Anonymous Sender ")); err != nil { |
||||
return nil, err |
||||
} |
||||
// For v5 keys, the 20 leftmost octets of the fingerprint are used.
|
||||
if _, err := param.Write(fingerprint[:20]); err != nil { |
||||
return nil, err |
||||
} |
||||
if param.Len() - len(curveOID) != 45 { |
||||
return nil, errors.New("ecdh: malformed KDF Param") |
||||
} |
||||
|
||||
// MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
|
||||
h := pub.KDF.Hash.New() |
||||
if _, err := h.Write([]byte{0x0, 0x0, 0x0, 0x1}); err != nil { |
||||
return nil, err |
||||
} |
||||
zbLen := len(zb) |
||||
i := 0 |
||||
j := zbLen - 1 |
||||
if stripLeading { |
||||
// Work around old go crypto bug where the leading zeros are missing.
|
||||
for ; i < zbLen && zb[i] == 0; i++ {} |
||||
} |
||||
if stripTrailing { |
||||
// Work around old OpenPGP.js bug where insignificant trailing zeros in
|
||||
// this little-endian number are missing.
|
||||
// (See https://github.com/openpgpjs/openpgpjs/pull/853.)
|
||||
for ; j >= 0 && zb[j] == 0; j-- {} |
||||
} |
||||
if _, err := h.Write(zb[i:j+1]); err != nil { |
||||
return nil, err |
||||
} |
||||
if _, err := h.Write(param.Bytes()); err != nil { |
||||
return nil, err |
||||
} |
||||
mb := h.Sum(nil) |
||||
|
||||
return mb[:pub.KDF.Cipher.KeySize()], nil // return oBits leftmost bits of MB.
|
||||
|
||||
} |
@ -0,0 +1,157 @@ |
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ecdh implements ECDH encryption, suitable for OpenPGP,
|
||||
// as specified in RFC 6637, section 8.
|
||||
package ecdh |
||||
|
||||
import ( |
||||
"errors" |
||||
"io" |
||||
"math/big" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc" |
||||
"golang.org/x/crypto/curve25519" |
||||
) |
||||
|
||||
// Generates a private-public key-pair.
|
||||
// 'priv' is a private key; a scalar belonging to the set
|
||||
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of the
|
||||
// curve. 'pub' is simply 'priv' * G where G is the base point.
|
||||
// See https://cr.yp.to/ecdh.html and RFC7748, sec 5.
|
||||
func x25519GenerateKeyPairBytes(rand io.Reader) (priv [32]byte, pub [32]byte, err error) { |
||||
var n, helper = new(big.Int), new(big.Int) |
||||
n.SetUint64(1) |
||||
n.Lsh(n, 252) |
||||
helper.SetString("27742317777372353535851937790883648493", 10) |
||||
n.Add(n, helper) |
||||
|
||||
for true { |
||||
_, err = io.ReadFull(rand, priv[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
// The following ensures that the private key is a number of the form
|
||||
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of
|
||||
// of the curve.
|
||||
priv[0] &= 248 |
||||
priv[31] &= 127 |
||||
priv[31] |= 64 |
||||
|
||||
// If the scalar is out of range, sample another random number.
|
||||
if new(big.Int).SetBytes(priv[:]).Cmp(n) >= 0 { |
||||
continue |
||||
} |
||||
|
||||
curve25519.ScalarBaseMult(&pub, &priv) |
||||
return |
||||
} |
||||
return |
||||
} |
||||
|
||||
// X25519GenerateKey samples the key pair according to the correct distribution.
|
||||
// It also sets the given key-derivation function and returns the *PrivateKey
|
||||
// object along with an error.
|
||||
func X25519GenerateKey(rand io.Reader, kdf KDF) (priv *PrivateKey, err error) { |
||||
ci := ecc.FindByName("Curve25519") |
||||
priv = new(PrivateKey) |
||||
priv.PublicKey.Curve = ci.Curve |
||||
d, pubKey, err := x25519GenerateKeyPairBytes(rand) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
priv.PublicKey.KDF = kdf |
||||
priv.D = make([]byte, 32) |
||||
copyReversed(priv.D, d[:]) |
||||
priv.PublicKey.CurveType = ci.CurveType |
||||
priv.PublicKey.Curve = ci.Curve |
||||
/* |
||||
* Note that ECPoint.point differs from the definition of public keys in |
||||
* [Curve25519] in two ways: (1) the byte-ordering is big-endian, which is |
||||
* more uniform with how big integers are represented in TLS, and (2) there |
||||
* is an additional length byte (so ECpoint.point is actually 33 bytes), |
||||
* again for uniformity (and extensibility). |
||||
*/ |
||||
var encodedKey = make([]byte, 33) |
||||
encodedKey[0] = 0x40 |
||||
copy(encodedKey[1:], pubKey[:]) |
||||
priv.PublicKey.X = new(big.Int).SetBytes(encodedKey[:]) |
||||
priv.PublicKey.Y = new(big.Int) |
||||
return priv, nil |
||||
} |
||||
|
||||
func X25519Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) { |
||||
d, ephemeralKey, err := x25519GenerateKeyPairBytes(random) |
||||
if err != nil { |
||||
return nil, nil, err |
||||
} |
||||
var pubKey [32]byte |
||||
|
||||
if pub.X.BitLen() > 33*264 { |
||||
return nil, nil, errors.New("ecdh: invalid key") |
||||
} |
||||
copy(pubKey[:], pub.X.Bytes()[1:]) |
||||
|
||||
var zb [32]byte |
||||
curve25519.ScalarBaseMult(&zb, &d) |
||||
curve25519.ScalarMult(&zb, &d, &pubKey) |
||||
z, err := buildKey(pub, zb[:], curveOID, fingerprint, false, false) |
||||
|
||||
if err != nil { |
||||
return nil, nil, err |
||||
} |
||||
|
||||
if c, err = keywrap.Wrap(z, msg); err != nil { |
||||
return nil, nil, err |
||||
} |
||||
|
||||
var vsg [33]byte |
||||
vsg[0] = 0x40 |
||||
copy(vsg[1:], ephemeralKey[:]) |
||||
|
||||
return vsg[:], c, nil |
||||
} |
||||
|
||||
func X25519Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) { |
||||
var zb, d, ephemeralKey [32]byte |
||||
if len(vsG) != 33 || vsG[0] != 0x40 { |
||||
return nil, errors.New("ecdh: invalid key") |
||||
} |
||||
copy(ephemeralKey[:], vsG[1:33]) |
||||
|
||||
copyReversed(d[:], priv.D) |
||||
curve25519.ScalarBaseMult(&zb, &d) |
||||
curve25519.ScalarMult(&zb, &d, &ephemeralKey) |
||||
|
||||
var c []byte |
||||
|
||||
for i := 0; i < 3; i++ { |
||||
// Try buildKey three times for compat, see comments in buildKey.
|
||||
z, err := buildKey(&priv.PublicKey, zb[:], curveOID, fingerprint, i == 1, i == 2) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
res, err := keywrap.Unwrap(z, m) |
||||
if i == 2 && err != nil { |
||||
// Only return an error after we've tried all variants of buildKey.
|
||||
return nil, err |
||||
} |
||||
|
||||
c = res |
||||
if err == nil { |
||||
break |
||||
} |
||||
} |
||||
|
||||
return c[:len(c)-int(c[len(c)-1])], nil |
||||
} |
||||
|
||||
func copyReversed(out []byte, in []byte) { |
||||
l := len(in) |
||||
for i := 0; i < l; i++ { |
||||
out[i] = in[l-i-1] |
||||
} |
||||
} |
@ -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 |
||||
} |
@ -0,0 +1,116 @@ |
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package errors contains common error types for the OpenPGP packages.
|
||||
package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
|
||||
import ( |
||||
"strconv" |
||||
) |
||||
|
||||
// A StructuralError is returned when OpenPGP data is found to be syntactically
|
||||
// invalid.
|
||||
type StructuralError string |
||||
|
||||
func (s StructuralError) Error() string { |
||||
return "openpgp: invalid data: " + string(s) |
||||
} |
||||
|
||||
// UnsupportedError indicates that, although the OpenPGP data is valid, it
|
||||
// makes use of currently unimplemented features.
|
||||
type UnsupportedError string |
||||
|
||||
func (s UnsupportedError) Error() string { |
||||
return "openpgp: unsupported feature: " + string(s) |
||||
} |
||||
|
||||
// InvalidArgumentError indicates that the caller is in error and passed an
|
||||
// incorrect value.
|
||||
type InvalidArgumentError string |
||||
|
||||
func (i InvalidArgumentError) Error() string { |
||||
return "openpgp: invalid argument: " + string(i) |
||||
} |
||||
|
||||
// SignatureError indicates that a syntactically valid signature failed to
|
||||
// validate.
|
||||
type SignatureError string |
||||
|
||||
func (b SignatureError) Error() string { |
||||
return "openpgp: invalid signature: " + string(b) |
||||
} |
||||
|
||||
var ErrMDCHashMismatch error = SignatureError("MDC hash mismatch") |
||||
var ErrMDCMissing error = SignatureError("MDC packet not found") |
||||
|
||||
type signatureExpiredError int |
||||
|
||||
func (se signatureExpiredError) Error() string { |
||||
return "openpgp: signature expired" |
||||
} |
||||
|
||||
var ErrSignatureExpired error = signatureExpiredError(0) |
||||
|
||||
type keyExpiredError int |
||||
|
||||
func (ke keyExpiredError) Error() string { |
||||
return "openpgp: key expired" |
||||
} |
||||
|
||||
var ErrKeyExpired error = keyExpiredError(0) |
||||
|
||||
type keyIncorrectError int |
||||
|
||||
func (ki keyIncorrectError) Error() string { |
||||
return "openpgp: incorrect key" |
||||
} |
||||
|
||||
var ErrKeyIncorrect error = keyIncorrectError(0) |
||||
|
||||
// KeyInvalidError indicates that the public key parameters are invalid
|
||||
// as they do not match the private ones
|
||||
type KeyInvalidError string |
||||
|
||||
func (e KeyInvalidError) Error() string { |
||||
return "openpgp: invalid key: " + string(e) |
||||
} |
||||
|
||||
type unknownIssuerError int |
||||
|
||||
func (unknownIssuerError) Error() string { |
||||
return "openpgp: signature made by unknown entity" |
||||
} |
||||
|
||||
var ErrUnknownIssuer error = unknownIssuerError(0) |
||||
|
||||
type keyRevokedError int |
||||
|
||||
func (keyRevokedError) Error() string { |
||||
return "openpgp: signature made by revoked key" |
||||
} |
||||
|
||||
var ErrKeyRevoked error = keyRevokedError(0) |
||||
|
||||
type UnknownPacketTypeError uint8 |
||||
|
||||
func (upte UnknownPacketTypeError) Error() string { |
||||
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) |
||||
} |
||||
|
||||
// AEADError indicates that there is a problem when initializing or using a
|
||||
// AEAD instance, configuration struct, nonces or index values.
|
||||
type AEADError string |
||||
|
||||
func (ae AEADError) Error() string { |
||||
return "openpgp: aead error: " + string(ae) |
||||
} |
||||
|
||||
// ErrDummyPrivateKey results when operations are attempted on a private key
|
||||
// that is just a dummy key. See
|
||||
// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109
|
||||
type ErrDummyPrivateKey string |
||||
|
||||
func (dke ErrDummyPrivateKey) Error() string { |
||||
return "openpgp: s2k GNU dummy key: " + string(dke) |
||||
} |
@ -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(100) |
||||
) |
||||
|
||||
// TagLength returns the length in bytes of authentication tags.
|
||||
func (mode AEADMode) TagLength() int { |
||||
switch mode { |
||||
case AEADModeEAX: |
||||
return 16 |
||||
case AEADModeOCB: |
||||
return 16 |
||||
case AEADModeGCM: |
||||
return 16 |
||||
default: |
||||
return 0 |
||||
} |
||||
} |
||||
|
||||
// NonceLength returns the length in bytes of nonces.
|
||||
func (mode AEADMode) NonceLength() int { |
||||
switch mode { |
||||
case AEADModeEAX: |
||||
return 16 |
||||
case AEADModeOCB: |
||||
return 15 |
||||
case AEADModeGCM: |
||||
return 12 |
||||
default: |
||||
return 0 |
||||
} |
||||
} |
||||
|
||||
// New returns a fresh instance of the given mode
|
||||
func (mode AEADMode) New(block cipher.Block) (alg cipher.AEAD) { |
||||
var err error |
||||
switch mode { |
||||
case AEADModeEAX: |
||||
alg, err = eax.NewEAX(block) |
||||
case AEADModeOCB: |
||||
alg, err = ocb.NewOCB(block) |
||||
case AEADModeGCM: |
||||
alg, err = cipher.NewGCM(block) |
||||
} |
||||
if err != nil { |
||||
panic(err.Error()) |
||||
} |
||||
return alg |
||||
} |
@ -0,0 +1,107 @@ |
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package algorithm |
||||
|
||||
import ( |
||||
"crypto/aes" |
||||
"crypto/cipher" |
||||
"crypto/des" |
||||
|
||||
"golang.org/x/crypto/cast5" |
||||
) |
||||
|
||||
// Cipher is an official symmetric key cipher algorithm. See RFC 4880,
|
||||
// section 9.2.
|
||||
type Cipher interface { |
||||
// Id returns the algorithm ID, as a byte, of the cipher.
|
||||
Id() uint8 |
||||
// KeySize returns the key size, in bytes, of the cipher.
|
||||
KeySize() int |
||||
// BlockSize returns the block size, in bytes, of the cipher.
|
||||
BlockSize() int |
||||
// New returns a fresh instance of the given cipher.
|
||||
New(key []byte) cipher.Block |
||||
} |
||||
|
||||
// The following constants mirror the OpenPGP standard (RFC 4880).
|
||||
const ( |
||||
TripleDES = CipherFunction(2) |
||||
CAST5 = CipherFunction(3) |
||||
AES128 = CipherFunction(7) |
||||
AES192 = CipherFunction(8) |
||||
AES256 = CipherFunction(9) |
||||
) |
||||
|
||||
// CipherById represents the different block ciphers specified for OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
|
||||
var CipherById = map[uint8]Cipher{ |
||||
TripleDES.Id(): TripleDES, |
||||
CAST5.Id(): CAST5, |
||||
AES128.Id(): AES128, |
||||
AES192.Id(): AES192, |
||||
AES256.Id(): AES256, |
||||
} |
||||
|
||||
type CipherFunction uint8 |
||||
|
||||
// ID returns the algorithm Id, as a byte, of cipher.
|
||||
func (sk CipherFunction) Id() uint8 { |
||||
return uint8(sk) |
||||
} |
||||
|
||||
var keySizeByID = map[uint8]int{ |
||||
TripleDES.Id(): 24, |
||||
CAST5.Id(): cast5.KeySize, |
||||
AES128.Id(): 16, |
||||
AES192.Id(): 24, |
||||
AES256.Id(): 32, |
||||
} |
||||
|
||||
// KeySize returns the key size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) KeySize() int { |
||||
switch cipher { |
||||
case TripleDES: |
||||
return 24 |
||||
case CAST5: |
||||
return cast5.KeySize |
||||
case AES128: |
||||
return 16 |
||||
case AES192: |
||||
return 24 |
||||
case AES256: |
||||
return 32 |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
// BlockSize returns the block size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) BlockSize() int { |
||||
switch cipher { |
||||
case TripleDES: |
||||
return des.BlockSize |
||||
case CAST5: |
||||
return 8 |
||||
case AES128, AES192, AES256: |
||||
return 16 |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
// New returns a fresh instance of the given cipher.
|
||||
func (cipher CipherFunction) New(key []byte) (block cipher.Block) { |
||||
var err error |
||||
switch cipher { |
||||
case TripleDES: |
||||
block, err = des.NewTripleDESCipher(key) |
||||
case CAST5: |
||||
block, err = cast5.NewCipher(key) |
||||
case AES128, AES192, AES256: |
||||
block, err = aes.NewCipher(key) |
||||
} |
||||
if err != nil { |
||||
panic(err.Error()) |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,86 @@ |
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package algorithm |
||||
|
||||
import ( |
||||
"crypto" |
||||
"fmt" |
||||
"hash" |
||||
) |
||||
|
||||
// Hash is an official hash function algorithm. See RFC 4880, section 9.4.
|
||||
type Hash interface { |
||||
// Id returns the algorithm ID, as a byte, of Hash.
|
||||
Id() uint8 |
||||
// Available reports whether the given hash function is linked into the binary.
|
||||
Available() bool |
||||
// HashFunc simply returns the value of h so that Hash implements SignerOpts.
|
||||
HashFunc() crypto.Hash |
||||
// New returns a new hash.Hash calculating the given hash function. New
|
||||
// panics if the hash function is not linked into the binary.
|
||||
New() hash.Hash |
||||
// Size returns the length, in bytes, of a digest resulting from the given
|
||||
// hash function. It doesn't require that the hash function in question be
|
||||
// linked into the program.
|
||||
Size() int |
||||
// String is the name of the hash function corresponding to the given
|
||||
// OpenPGP hash id.
|
||||
String() string |
||||
} |
||||
|
||||
// The following vars mirror the crypto/Hash supported hash functions.
|
||||
var ( |
||||
MD5 Hash = cryptoHash{1, crypto.MD5} |
||||
SHA1 Hash = cryptoHash{2, crypto.SHA1} |
||||
RIPEMD160 Hash = cryptoHash{3, crypto.RIPEMD160} |
||||
SHA256 Hash = cryptoHash{8, crypto.SHA256} |
||||
SHA384 Hash = cryptoHash{9, crypto.SHA384} |
||||
SHA512 Hash = cryptoHash{10, crypto.SHA512} |
||||
SHA224 Hash = cryptoHash{11, crypto.SHA224} |
||||
) |
||||
|
||||
// HashById represents the different hash functions specified for OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14
|
||||
var ( |
||||
HashById = map[uint8]Hash{ |
||||
MD5.Id(): MD5, |
||||
SHA1.Id(): SHA1, |
||||
RIPEMD160.Id(): RIPEMD160, |
||||
SHA256.Id(): SHA256, |
||||
SHA384.Id(): SHA384, |
||||
SHA512.Id(): SHA512, |
||||
SHA224.Id(): SHA224, |
||||
} |
||||
) |
||||
|
||||
// cryptoHash contains pairs relating OpenPGP's hash identifier with
|
||||
// Go's crypto.Hash type. See RFC 4880, section 9.4.
|
||||
type cryptoHash struct { |
||||
id uint8 |
||||
crypto.Hash |
||||
} |
||||
|
||||
// Id returns the algorithm ID, as a byte, of cryptoHash.
|
||||
func (h cryptoHash) Id() uint8 { |
||||
return h.id |
||||
} |
||||
|
||||
var hashNames = map[uint8]string{ |
||||
MD5.Id(): "MD5", |
||||
SHA1.Id(): "SHA1", |
||||
RIPEMD160.Id(): "RIPEMD160", |
||||
SHA256.Id(): "SHA256", |
||||
SHA384.Id(): "SHA384", |
||||
SHA512.Id(): "SHA512", |
||||
SHA224.Id(): "SHA224", |
||||
} |
||||
|
||||
func (h cryptoHash) String() string { |
||||
s, ok := hashNames[h.id] |
||||
if !ok { |
||||
panic(fmt.Sprintf("Unsupported hash function %d", h.id)) |
||||
} |
||||
return s |
||||
} |
@ -0,0 +1,118 @@ |
||||
package ecc |
||||
|
||||
import ( |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding" |
||||
"crypto/elliptic" |
||||
"bytes" |
||||
"github.com/ProtonMail/go-crypto/bitcurves" |
||||
"github.com/ProtonMail/go-crypto/brainpool" |
||||
) |
||||
|
||||
type SignatureAlgorithm uint8 |
||||
|
||||
const ( |
||||
ECDSA SignatureAlgorithm = 1 |
||||
EdDSA SignatureAlgorithm = 2 |
||||
) |
||||
|
||||
type CurveInfo struct { |
||||
Name string |
||||
Oid *encoding.OID |
||||
Curve elliptic.Curve |
||||
SigAlgorithm SignatureAlgorithm |
||||
CurveType CurveType |
||||
} |
||||
|
||||
var curves = []CurveInfo{ |
||||
{ |
||||
Name: "NIST curve P-256", |
||||
Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}), |
||||
Curve: elliptic.P256(), |
||||
CurveType: NISTCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "NIST curve P-384", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}), |
||||
Curve: elliptic.P384(), |
||||
CurveType: NISTCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "NIST curve P-521", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}), |
||||
Curve: elliptic.P521(), |
||||
CurveType: NISTCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "SecP256k1", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}), |
||||
Curve: bitcurves.S256(), |
||||
CurveType: BitCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "Curve25519", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}), |
||||
Curve: elliptic.P256(),// filler
|
||||
CurveType: Curve25519, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "Ed25519", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}), |
||||
Curve: elliptic.P256(), // filler
|
||||
CurveType: NISTCurve, |
||||
SigAlgorithm: EdDSA, |
||||
}, |
||||
{ |
||||
Name: "Brainpool P256r1", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}), |
||||
Curve: brainpool.P256r1(), |
||||
CurveType: BrainpoolCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "BrainpoolP384r1", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}), |
||||
Curve: brainpool.P384r1(), |
||||
CurveType: BrainpoolCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
{ |
||||
Name: "BrainpoolP512r1", |
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}), |
||||
Curve: brainpool.P512r1(), |
||||
CurveType: BrainpoolCurve, |
||||
SigAlgorithm: ECDSA, |
||||
}, |
||||
} |
||||
|
||||
func FindByCurve(curve elliptic.Curve) *CurveInfo { |
||||
for _, curveInfo := range curves { |
||||
if curveInfo.Curve == curve { |
||||
return &curveInfo |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func FindByOid(oid encoding.Field) *CurveInfo { |
||||
var rawBytes = oid.Bytes() |
||||
for _, curveInfo := range curves { |
||||
if bytes.Equal(curveInfo.Oid.Bytes(), rawBytes) { |
||||
return &curveInfo |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func FindByName(name string) *CurveInfo { |
||||
for _, curveInfo := range curves { |
||||
if curveInfo.Name == name { |
||||
return &curveInfo |
||||
} |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,10 @@ |
||||
package ecc |
||||
|
||||
type CurveType uint8 |
||||
|
||||
const ( |
||||
NISTCurve CurveType = 1 |
||||
Curve25519 CurveType = 2 |
||||
BitCurve CurveType = 3 |
||||
BrainpoolCurve CurveType = 4 |
||||
) |
@ -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) |
||||
} |
@ -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 |
||||
} |
@ -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 argment too large") |
||||
} |
||||
} |
||||
|
||||
return &OID{ |
||||
bytes: bytes, |
||||
} |
||||
} |
||||
|
||||
// Bytes returns the decoded data.
|
||||
func (o *OID) Bytes() []byte { |
||||
return o.bytes |
||||
} |
||||
|
||||
// BitLength is the size in bits of the decoded data.
|
||||
func (o *OID) BitLength() uint16 { |
||||
return uint16(len(o.bytes) * 8) |
||||
} |
||||
|
||||
// EncodedBytes returns the encoded data.
|
||||
func (o *OID) EncodedBytes() []byte { |
||||
return append([]byte{byte(len(o.bytes))}, o.bytes...) |
||||
} |
||||
|
||||
// EncodedLength is the size in bytes of the encoded data.
|
||||
func (o *OID) EncodedLength() uint16 { |
||||
return uint16(1 + len(o.bytes)) |
||||
} |
||||
|
||||
// ReadFrom reads into b the next OID from r.
|
||||
func (o *OID) ReadFrom(r io.Reader) (int64, error) { |
||||
var buf [1]byte |
||||
n, err := io.ReadFull(r, buf[:]) |
||||
if err != nil { |
||||
if err == io.EOF { |
||||
err = io.ErrUnexpectedEOF |
||||
} |
||||
return int64(n), err |
||||
} |
||||
|
||||
switch buf[0] { |
||||
case reservedOIDLength1, reservedOIDLength2: |
||||
return int64(n), errors.UnsupportedError("reserved for future extensions") |
||||
} |
||||
|
||||
o.bytes = make([]byte, buf[0]) |
||||
|
||||
nn, err := io.ReadFull(r, o.bytes) |
||||
if err == io.EOF { |
||||
err = io.ErrUnexpectedEOF |
||||
} |
||||
|
||||
return int64(n) + int64(nn), err |
||||
} |
@ -0,0 +1,375 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package openpgp |
||||
|
||||
import ( |
||||
"crypto" |
||||
"crypto/rand" |
||||
"crypto/rsa" |
||||
goerrors "errors" |
||||
"io" |
||||
"math/big" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh" |
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" |
||||
"github.com/ProtonMail/go-crypto/openpgp/packet" |
||||
"golang.org/x/crypto/ed25519" |
||||
) |
||||
|
||||
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
|
||||
// single identity composed of the given full name, comment and email, any of
|
||||
// which may be empty but must not contain any of "()<>\x00".
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { |
||||
creationTime := config.Now() |
||||
keyLifetimeSecs := config.KeyLifetime() |
||||
|
||||
uid := packet.NewUserId(name, comment, email) |
||||
if uid == nil { |
||||
return nil, errors.InvalidArgumentError("user id field contained invalid characters") |
||||
} |
||||
|
||||
// Generate a primary signing key
|
||||
primaryPrivRaw, err := newSigner(config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw) |
||||
if config != nil && config.V5Keys { |
||||
primary.UpgradeToV5() |
||||
} |
||||
|
||||
isPrimaryId := true |
||||
selfSignature := &packet.Signature{ |
||||
Version: primary.PublicKey.Version, |
||||
SigType: packet.SigTypePositiveCert, |
||||
PubKeyAlgo: primary.PublicKey.PubKeyAlgo, |
||||
Hash: config.Hash(), |
||||
CreationTime: creationTime, |
||||
KeyLifetimeSecs: &keyLifetimeSecs, |
||||
IssuerKeyId: &primary.PublicKey.KeyId, |
||||
IssuerFingerprint: primary.PublicKey.Fingerprint, |
||||
IsPrimaryId: &isPrimaryId, |
||||
FlagsValid: true, |
||||
FlagSign: true, |
||||
FlagCertify: true, |
||||
MDC: true, // true by default, see 5.8 vs. 5.14
|
||||
AEAD: config.AEAD() != nil, |
||||
V5Keys: config != nil && config.V5Keys, |
||||
} |
||||
|
||||
// Set the PreferredHash for the SelfSignature from the packet.Config.
|
||||
// If it is not the must-implement algorithm from rfc4880bis, append that.
|
||||
selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())} |
||||
if config.Hash() != crypto.SHA256 { |
||||
selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256)) |
||||
} |
||||
|
||||
// Likewise for DefaultCipher.
|
||||
selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())} |
||||
if config.Cipher() != packet.CipherAES128 { |
||||
selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128)) |
||||
} |
||||
|
||||
// And for DefaultMode.
|
||||
selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())} |
||||
if config.AEAD().Mode() != packet.AEADModeEAX { |
||||
selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX)) |
||||
} |
||||
|
||||
// User ID binding signature
|
||||
err = selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
// Generate an encryption subkey
|
||||
subPrivRaw, err := newDecrypter(config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw) |
||||
sub.IsSubkey = true |
||||
sub.PublicKey.IsSubkey = true |
||||
if config != nil && config.V5Keys { |
||||
sub.UpgradeToV5() |
||||
} |
||||
|
||||
// NOTE: No KeyLifetimeSecs here, but we will not return this subkey in EncryptionKey()
|
||||
// if the primary/master key has expired.
|
||||
subKey := Subkey{ |
||||
PublicKey: &sub.PublicKey, |
||||
PrivateKey: sub, |
||||
Sig: &packet.Signature{ |
||||
Version: primary.PublicKey.Version, |
||||
CreationTime: creationTime, |
||||
SigType: packet.SigTypeSubkeyBinding, |
||||
PubKeyAlgo: primary.PublicKey.PubKeyAlgo, |
||||
Hash: config.Hash(), |
||||
FlagsValid: true, |
||||
FlagEncryptStorage: true, |
||||
FlagEncryptCommunications: true, |
||||
IssuerKeyId: &primary.PublicKey.KeyId, |
||||
}, |
||||
} |
||||
|
||||
// Subkey binding signature
|
||||
err = subKey.Sig.SignKey(subKey.PublicKey, primary, config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return &Entity{ |
||||
PrimaryKey: &primary.PublicKey, |
||||
PrivateKey: primary, |
||||
Identities: map[string]*Identity{ |
||||
uid.Id: &Identity{ |
||||
Name: uid.Id, |
||||
UserId: uid, |
||||
SelfSignature: selfSignature, |
||||
Signatures: []*packet.Signature{selfSignature}, |
||||
}, |
||||
}, |
||||
Subkeys: []Subkey{subKey}, |
||||
}, nil |
||||
} |
||||
|
||||
// AddSigningSubkey adds a signing keypair as a subkey to the Entity.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) AddSigningSubkey(config *packet.Config) error { |
||||
creationTime := config.Now() |
||||
keyLifetimeSecs := config.KeyLifetime() |
||||
|
||||
subPrivRaw, err := newSigner(config) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw) |
||||
|
||||
subkey := Subkey{ |
||||
PublicKey: &sub.PublicKey, |
||||
PrivateKey: sub, |
||||
Sig: &packet.Signature{ |
||||
Version: e.PrimaryKey.Version, |
||||
CreationTime: creationTime, |
||||
KeyLifetimeSecs: &keyLifetimeSecs, |
||||
SigType: packet.SigTypeSubkeyBinding, |
||||
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo, |
||||
Hash: config.Hash(), |
||||
FlagsValid: true, |
||||
FlagSign: true, |
||||
IssuerKeyId: &e.PrimaryKey.KeyId, |
||||
EmbeddedSignature: &packet.Signature{ |
||||
Version: e.PrimaryKey.Version, |
||||
CreationTime: creationTime, |
||||
SigType: packet.SigTypePrimaryKeyBinding, |
||||
PubKeyAlgo: sub.PublicKey.PubKeyAlgo, |
||||
Hash: config.Hash(), |
||||
IssuerKeyId: &e.PrimaryKey.KeyId, |
||||
}, |
||||
}, |
||||
} |
||||
if config != nil && config.V5Keys { |
||||
subkey.PublicKey.UpgradeToV5() |
||||
} |
||||
|
||||
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
subkey.PublicKey.IsSubkey = true |
||||
subkey.PrivateKey.IsSubkey = true |
||||
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil { |
||||
return err |
||||
} |
||||
|
||||
e.Subkeys = append(e.Subkeys, subkey) |
||||
return nil |
||||
} |
||||
|
||||
// AddEncryptionSubkey adds an encryption keypair as a subkey to the Entity.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) AddEncryptionSubkey(config *packet.Config) error { |
||||
creationTime := config.Now() |
||||
keyLifetimeSecs := config.KeyLifetime() |
||||
|
||||
subPrivRaw, err := newDecrypter(config) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw) |
||||
|
||||
subkey := Subkey{ |
||||
PublicKey: &sub.PublicKey, |
||||
PrivateKey: sub, |
||||
Sig: &packet.Signature{ |
||||
Version: e.PrimaryKey.Version, |
||||
CreationTime: creationTime, |
||||
KeyLifetimeSecs: &keyLifetimeSecs, |
||||
SigType: packet.SigTypeSubkeyBinding, |
||||
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo, |
||||
Hash: config.Hash(), |
||||
FlagsValid: true, |
||||
FlagEncryptStorage: true, |
||||
FlagEncryptCommunications: true, |
||||
IssuerKeyId: &e.PrimaryKey.KeyId, |
||||
}, |
||||
} |
||||
if config != nil && config.V5Keys { |
||||
subkey.PublicKey.UpgradeToV5() |
||||
} |
||||
|
||||
subkey.PublicKey.IsSubkey = true |
||||
subkey.PrivateKey.IsSubkey = true |
||||
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil { |
||||
return err |
||||
} |
||||
|
||||
e.Subkeys = append(e.Subkeys, subkey) |
||||
return nil |
||||
} |
||||
|
||||
// Generates a signing key
|
||||
func newSigner(config *packet.Config) (signer crypto.Signer, err error) { |
||||
switch config.PublicKeyAlgorithm() { |
||||
case packet.PubKeyAlgoRSA: |
||||
bits := config.RSAModulusBits() |
||||
if bits < 1024 { |
||||
return nil, errors.InvalidArgumentError("bits must be >= 1024") |
||||
} |
||||
if config != nil && len(config.RSAPrimes) >= 2 { |
||||
primes := config.RSAPrimes[0:2] |
||||
config.RSAPrimes = config.RSAPrimes[2:] |
||||
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes) |
||||
} |
||||
return rsa.GenerateKey(config.Random(), bits) |
||||
case packet.PubKeyAlgoEdDSA: |
||||
_, priv, err := ed25519.GenerateKey(config.Random()) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &priv, nil |
||||
default: |
||||
return nil, errors.InvalidArgumentError("unsupported public key algorithm") |
||||
} |
||||
} |
||||
|
||||
// Generates an encryption/decryption key
|
||||
func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { |
||||
switch config.PublicKeyAlgorithm() { |
||||
case packet.PubKeyAlgoRSA: |
||||
bits := config.RSAModulusBits() |
||||
if bits < 1024 { |
||||
return nil, errors.InvalidArgumentError("bits must be >= 1024") |
||||
} |
||||
if config != nil && len(config.RSAPrimes) >= 2 { |
||||
primes := config.RSAPrimes[0:2] |
||||
config.RSAPrimes = config.RSAPrimes[2:] |
||||
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes) |
||||
} |
||||
return rsa.GenerateKey(config.Random(), bits) |
||||
case packet.PubKeyAlgoEdDSA: |
||||
fallthrough // When passing EdDSA, we generate an ECDH subkey
|
||||
case packet.PubKeyAlgoECDH: |
||||
var kdf = ecdh.KDF{ |
||||
Hash: algorithm.SHA512, |
||||
Cipher: algorithm.AES256, |
||||
} |
||||
return ecdh.X25519GenerateKey(config.Random(), kdf) |
||||
default: |
||||
return nil, errors.InvalidArgumentError("unsupported public key algorithm") |
||||
} |
||||
} |
||||
|
||||
var bigOne = big.NewInt(1) |
||||
|
||||
// generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the
|
||||
// given bit size, using the given random source and prepopulated primes.
|
||||
func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) { |
||||
priv := new(rsa.PrivateKey) |
||||
priv.E = 65537 |
||||
|
||||
if nprimes < 2 { |
||||
return nil, goerrors.New("generateRSAKeyWithPrimes: nprimes must be >= 2") |
||||
} |
||||
|
||||
if bits < 1024 { |
||||
return nil, goerrors.New("generateRSAKeyWithPrimes: bits must be >= 1024") |
||||
} |
||||
|
||||
primes := make([]*big.Int, nprimes) |
||||
|
||||
NextSetOfPrimes: |
||||
for { |
||||
todo := bits |
||||
// crypto/rand should set the top two bits in each prime.
|
||||
// Thus each prime has the form
|
||||
// p_i = 2^bitlen(p_i) × 0.11... (in base 2).
|
||||
// And the product is:
|
||||
// P = 2^todo × α
|
||||
// where α is the product of nprimes numbers of the form 0.11...
|
||||
//
|
||||
// If α < 1/2 (which can happen for nprimes > 2), we need to
|
||||
// shift todo to compensate for lost bits: the mean value of 0.11...
|
||||
// is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
|
||||
// will give good results.
|
||||
if nprimes >= 7 { |
||||
todo += (nprimes - 2) / 5 |
||||
} |
||||
for i := 0; i < nprimes; i++ { |
||||
var err error |
||||
if len(prepopulatedPrimes) == 0 { |
||||
primes[i], err = rand.Prime(random, todo/(nprimes-i)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} else { |
||||
primes[i] = prepopulatedPrimes[0] |
||||
prepopulatedPrimes = prepopulatedPrimes[1:] |
||||
} |
||||
|
||||
todo -= primes[i].BitLen() |
||||
} |
||||
|
||||
// Make sure that primes is pairwise unequal.
|
||||
for i, prime := range primes { |
||||
for j := 0; j < i; j++ { |
||||
if prime.Cmp(primes[j]) == 0 { |
||||
continue NextSetOfPrimes |
||||
} |
||||
} |
||||
} |
||||
|
||||
n := new(big.Int).Set(bigOne) |
||||
totient := new(big.Int).Set(bigOne) |
||||
pminus1 := new(big.Int) |
||||
for _, prime := range primes { |
||||
n.Mul(n, prime) |
||||
pminus1.Sub(prime, bigOne) |
||||
totient.Mul(totient, pminus1) |
||||
} |
||||
if n.BitLen() != bits { |
||||
// This should never happen for nprimes == 2 because
|
||||
// crypto/rand should set the top two bits in each prime.
|
||||
// For nprimes > 2 we hope it does not happen often.
|
||||
continue NextSetOfPrimes |
||||
} |
||||
|
||||
priv.D = new(big.Int) |
||||
e := big.NewInt(int64(priv.E)) |
||||
ok := priv.D.ModInverse(e, totient) |
||||
|
||||
if ok != nil { |
||||
priv.Primes = primes |
||||
priv.N = n |
||||
break |
||||
} |
||||
} |
||||
|
||||
priv.Precompute() |
||||
return priv, nil |
||||
} |
@ -0,0 +1,707 @@ |
||||
// 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" |
||||
"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 |
||||
} |
||||
|
||||
// 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 |
||||
Signatures []*packet.Signature |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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 the Identity marked as primary or the first identity
|
||||
// if none are so marked.
|
||||
func (e *Entity) PrimaryIdentity() *Identity { |
||||
var firstIdentity *Identity |
||||
for _, ident := range e.Identities { |
||||
if firstIdentity == nil { |
||||
firstIdentity = ident |
||||
} |
||||
if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { |
||||
return ident |
||||
} |
||||
} |
||||
return firstIdentity |
||||
} |
||||
|
||||
// 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 primary key has expired.
|
||||
i := e.PrimaryIdentity() |
||||
primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now) |
||||
if primaryKeyExpired { |
||||
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) && |
||||
(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}, true |
||||
} |
||||
|
||||
// If we don't have any candidate subkeys for encryption and
|
||||
// the primary key doesn't have any usage metadata then we
|
||||
// assume that the primary key is ok. Or, if the primary key is
|
||||
// marked as ok to encrypt with, then we can obviously use it.
|
||||
// Also, check expiry again just to be safe.
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && |
||||
e.PrimaryKey.PubKeyAlgo.CanEncrypt() && !primaryKeyExpired { |
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true |
||||
} |
||||
|
||||
return Key{}, false |
||||
} |
||||
|
||||
// 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) { |
||||
// Fail to find any signing key if the primary key has expired.
|
||||
i := e.PrimaryIdentity() |
||||
primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now) |
||||
if primaryKeyExpired { |
||||
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 && |
||||
subkey.Sig.FlagSign && |
||||
subkey.PublicKey.PubKeyAlgo.CanSign() && |
||||
!subkey.PublicKey.KeyExpired(subkey.Sig, now) && |
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) && |
||||
(id == 0 || subkey.PrivateKey.KeyId == id) { |
||||
candidateSubkey = idx |
||||
maxTime = subkey.Sig.CreationTime |
||||
} |
||||
} |
||||
|
||||
if candidateSubkey != -1 { |
||||
subkey := e.Subkeys[candidateSubkey] |
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true |
||||
} |
||||
|
||||
// If we have no candidate subkey then we assume that it's ok to sign
|
||||
// with the primary key. Or, if the primary key is marked as ok to
|
||||
// sign with, then we can use it. Also, check expiry again just to be safe.
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && |
||||
e.PrimaryKey.PubKeyAlgo.CanSign() && !primaryKeyExpired && |
||||
(id == 0 || e.PrivateKey.KeyId == id) { |
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true |
||||
} |
||||
|
||||
// No keys with a valid Signing Flag or no keys matched the id passed in
|
||||
return Key{}, false |
||||
} |
||||
|
||||
// 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 { |
||||
var selfSig *packet.Signature |
||||
for _, ident := range e.Identities { |
||||
if selfSig == nil { |
||||
selfSig = ident.SelfSignature |
||||
} else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { |
||||
selfSig = ident.SelfSignature |
||||
break |
||||
} |
||||
} |
||||
keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) |
||||
} |
||||
|
||||
for _, subKey := range e.Subkeys { |
||||
if subKey.PublicKey.KeyId == id { |
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) |
||||
} |
||||
} |
||||
} |
||||
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 len(key.Entity.Revocations) > 0 { |
||||
continue |
||||
} |
||||
|
||||
if key.SelfSignature.RevocationReason != nil { |
||||
continue |
||||
} |
||||
|
||||
if key.SelfSignature.FlagsValid && requiredUsage != 0 { |
||||
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}) |
||||
} |
||||
} |
||||
} |
||||
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 { |
||||
err = nil |
||||
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 |
||||
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 { |
||||
// TODO: RFC4880 5.2.1 permits signatures
|
||||
// directly on keys (eg. to bind additional
|
||||
// revocation keys).
|
||||
} |
||||
// Else, ignoring the signature as it does not follow anything
|
||||
// we would know to attach it to.
|
||||
case *packet.PrivateKey: |
||||
if pkt.IsSubkey == false { |
||||
packets.Unread(p) |
||||
break EachPacket |
||||
} |
||||
err = addSubkey(e, packets, &pkt.PublicKey, pkt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
case *packet.PublicKey: |
||||
if pkt.IsSubkey == false { |
||||
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 { |
||||
return nil, errors.StructuralError("entity without any identities") |
||||
} |
||||
|
||||
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.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && 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 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.Sig = sig |
||||
case packet.SigTypeSubkeyBinding: |
||||
if shouldReplaceSubkeySig(subKey.Sig, sig) { |
||||
subKey.Sig = sig |
||||
} |
||||
} |
||||
} |
||||
|
||||
if subKey.Sig == nil { |
||||
return errors.StructuralError("subkey packet not followed by signature") |
||||
} |
||||
|
||||
e.Subkeys = append(e.Subkeys, subKey) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool { |
||||
if potentialNewSig == nil { |
||||
return false |
||||
} |
||||
|
||||
if existingSig == nil { |
||||
return true |
||||
} |
||||
|
||||
if existingSig.SigType == packet.SigTypeSubkeyRevocation { |
||||
return false // never override a revocation signature
|
||||
} |
||||
|
||||
return potentialNewSig.CreationTime.After(existingSig.CreationTime) |
||||
} |
||||
|
||||
// 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 _, ident := range e.Identities { |
||||
err = ident.UserId.Serialize(w) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if reSign { |
||||
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
err = ident.SelfSignature.Serialize(w) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
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 |
||||
} |
||||
} |
||||
} |
||||
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 _, 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 |
||||
} |
||||
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 { |
||||
if signer.PrivateKey == nil { |
||||
return errors.InvalidArgumentError("signing Entity must have a private key") |
||||
} |
||||
if signer.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 := &packet.Signature{ |
||||
Version: signer.PrivateKey.Version, |
||||
SigType: packet.SigTypeGenericCert, |
||||
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, |
||||
Hash: config.Hash(), |
||||
CreationTime: config.Now(), |
||||
IssuerKeyId: &signer.PrivateKey.KeyId, |
||||
} |
||||
if err := sig.SignUserId(identity, e.PrimaryKey, signer.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 { |
||||
reasonCode := uint8(reason) |
||||
revSig := &packet.Signature{ |
||||
Version: e.PrimaryKey.Version, |
||||
CreationTime: config.Now(), |
||||
SigType: packet.SigTypeKeyRevocation, |
||||
PubKeyAlgo: packet.PubKeyAlgoRSA, |
||||
Hash: config.Hash(), |
||||
RevocationReason: &reasonCode, |
||||
RevocationReasonText: reasonText, |
||||
IssuerKeyId: &e.PrimaryKey.KeyId, |
||||
} |
||||
|
||||
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") |
||||
} |
||||
|
||||
reasonCode := uint8(reason) |
||||
revSig := &packet.Signature{ |
||||
Version: e.PrimaryKey.Version, |
||||
CreationTime: config.Now(), |
||||
SigType: packet.SigTypeSubkeyRevocation, |
||||
PubKeyAlgo: packet.PubKeyAlgoRSA, |
||||
Hash: config.Hash(), |
||||
RevocationReason: &reasonCode, |
||||
RevocationReasonText: reasonText, |
||||
IssuerKeyId: &e.PrimaryKey.KeyId, |
||||
} |
||||
|
||||
if err := revSig.RevokeKey(sk.PublicKey, e.PrivateKey, config); err != nil { |
||||
return err |
||||
} |
||||
|
||||
sk.Sig = revSig |
||||
return nil |
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,56 @@ |
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet |
||||
|
||||
import "math/bits" |
||||
|
||||
// AEADConfig collects a number of AEAD parameters along with sensible defaults.
|
||||
// A nil AEADConfig is valid and results in all default values.
|
||||
type AEADConfig struct { |
||||
// The AEAD mode of operation.
|
||||
DefaultMode AEADMode |
||||
// Amount of octets in each chunk of data
|
||||
ChunkSize uint64 |
||||
} |
||||
|
||||
// Mode returns the AEAD mode of operation.
|
||||
func (conf *AEADConfig) Mode() AEADMode { |
||||
if conf == nil || conf.DefaultMode == 0 { |
||||
return AEADModeEAX |
||||
} |
||||
mode := conf.DefaultMode |
||||
if mode != AEADModeEAX && mode != AEADModeOCB && |
||||
mode != AEADModeExperimentalGCM { |
||||
panic("AEAD mode unsupported") |
||||
} |
||||
return mode |
||||
} |
||||
|
||||
// ChunkSizeByte returns the byte indicating the chunk size. The effective
|
||||
// chunk size is computed with the formula uint64(1) << (chunkSizeByte + 6)
|
||||
func (conf *AEADConfig) ChunkSizeByte() byte { |
||||
if conf == nil || conf.ChunkSize == 0 { |
||||
return 12 // 1 << (12 + 6) == 262144 bytes
|
||||
} |
||||
|
||||
chunkSize := conf.ChunkSize |
||||
exponent := bits.Len64(chunkSize) - 1 |
||||
switch { |
||||
case exponent < 6: |
||||
exponent = 6 |
||||
case exponent > 27: |
||||
exponent = 27 |
||||
} |
||||
|
||||
return byte(exponent - 6) |
||||
} |
||||
|
||||
// decodeAEADChunkSize returns the effective chunk size. In 32-bit systems, the
|
||||
// maximum returned value is 1 << 30.
|
||||
func decodeAEADChunkSize(c byte) int { |
||||
size := uint64(1 << (c + 6)) |
||||
if size != uint64(int(size)) { |
||||
return 1 << 30 |
||||
} |
||||
return int(size) |
||||
} |
@ -0,0 +1,364 @@ |
||||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/cipher" |
||||
"crypto/rand" |
||||
"encoding/binary" |
||||
"io" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" |
||||
) |
||||
|
||||
// AEADEncrypted represents an AEAD Encrypted Packet (tag 20, RFC4880bis-5.16).
|
||||
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 |
||||
|
||||
// An AEAD opener/sealer, its configuration, and data for en/decryption.
|
||||
type aeadCrypter struct { |
||||
aead cipher.AEAD |
||||
chunkSize int |
||||
initialNonce []byte |
||||
associatedData []byte // Chunk-independent associated data
|
||||
chunkIndex []byte // Chunk counter
|
||||
bytesProcessed int // Amount of plaintext bytes encrypted/decrypted
|
||||
buffer bytes.Buffer // Buffered bytes accross chunks
|
||||
} |
||||
|
||||
// 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
|
||||
} |
||||
|
||||
// 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
|
||||
peekedBytes []byte // Used to detect last chunk
|
||||
eof bool |
||||
} |
||||
|
||||
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.NonceLength() |
||||
if nonceLen == 0 { |
||||
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 = byte(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
|
||||
tagLen := ae.mode.TagLength() |
||||
peekedBytes := make([]byte, 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()) |
||||
} |
||||
chunkSize := decodeAEADChunkSize(ae.chunkSizeByte) |
||||
return &aeadDecrypter{ |
||||
aeadCrypter: aeadCrypter{ |
||||
aead: aead, |
||||
chunkSize: chunkSize, |
||||
initialNonce: ae.initialNonce, |
||||
associatedData: ae.associatedData(), |
||||
chunkIndex: make([]byte, 8), |
||||
}, |
||||
reader: ae.Contents, |
||||
peekedBytes: peekedBytes}, nil |
||||
} |
||||
|
||||
// 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 ar.buffer.Len() > 0 { |
||||
return ar.buffer.Read(dst) |
||||
} |
||||
|
||||
// Return EOF if we've previously validated the final tag
|
||||
if ar.eof { |
||||
return 0, io.EOF |
||||
} |
||||
|
||||
// Read a chunk
|
||||
tagLen := ar.aead.Overhead() |
||||
cipherChunkBuf := new(bytes.Buffer) |
||||
_, errRead := io.CopyN(cipherChunkBuf, ar.reader, int64(ar.chunkSize + tagLen)) |
||||
cipherChunk := cipherChunkBuf.Bytes() |
||||
if errRead != nil && errRead != io.EOF { |
||||
return 0, errRead |
||||
} |
||||
decrypted, errChunk := ar.openChunk(cipherChunk) |
||||
if errChunk != nil { |
||||
return 0, errChunk |
||||
} |
||||
|
||||
// Return decrypted bytes, buffering if necessary
|
||||
if len(dst) < len(decrypted) { |
||||
n = copy(dst, decrypted[:len(dst)]) |
||||
ar.buffer.Write(decrypted[len(dst):]) |
||||
} else { |
||||
n = copy(dst, decrypted) |
||||
} |
||||
|
||||
// Check final authentication tag
|
||||
if errRead == io.EOF { |
||||
errChunk := ar.validateFinalTag(ar.peekedBytes) |
||||
if errChunk != nil { |
||||
return n, errChunk |
||||
} |
||||
ar.eof = true // Mark EOF for when we've returned all buffered data
|
||||
} |
||||
return |
||||
} |
||||
|
||||
// Close is noOp. The final authentication tag of the stream was already
|
||||
// checked in the last Read call. In the future, this function could be used to
|
||||
// wipe the reader and peeked, decrypted bytes, if necessary.
|
||||
func (ar *aeadDecrypter) Close() (err error) { |
||||
return nil |
||||
} |
||||
|
||||
// SerializeAEADEncrypted initializes the aeadCrypter and returns a writer.
|
||||
// This writer encrypts and writes bytes (see aeadEncrypter.Write()).
|
||||
func SerializeAEADEncrypted(w io.Writer, key []byte, cipher CipherFunction, mode AEADMode, config *Config) (io.WriteCloser, error) { |
||||
writeCloser := noOpCloser{w} |
||||
writer, err := serializeStreamHeader(writeCloser, packetTypeAEADEncrypted) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
// Data for en/decryption: tag, version, cipher, aead mode, chunk size
|
||||
aeadConf := config.AEAD() |
||||
prefix := []byte{ |
||||
0xD4, |
||||
aeadEncryptedVersion, |
||||
byte(config.Cipher()), |
||||
byte(aeadConf.Mode()), |
||||
aeadConf.ChunkSizeByte(), |
||||
} |
||||
n, err := writer.Write(prefix[1:]) |
||||
if err != nil || n < 4 { |
||||
return nil, errors.AEADError("could not write AEAD headers") |
||||
} |
||||
// Sample nonce
|
||||
nonceLen := aeadConf.Mode().NonceLength() |
||||
nonce := make([]byte, nonceLen) |
||||
n, err = rand.Read(nonce) |
||||
if err != nil { |
||||
panic("Could not sample random nonce") |
||||
} |
||||
_, err = writer.Write(nonce) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
blockCipher := CipherFunction(config.Cipher()).new(key) |
||||
alg := AEADMode(aeadConf.Mode()).new(blockCipher) |
||||
|
||||
chunkSize := decodeAEADChunkSize(aeadConf.ChunkSizeByte()) |
||||
return &aeadEncrypter{ |
||||
aeadCrypter: aeadCrypter{ |
||||
aead: alg, |
||||
chunkSize: chunkSize, |
||||
associatedData: prefix, |
||||
chunkIndex: make([]byte, 8), |
||||
initialNonce: nonce, |
||||
}, |
||||
writer: writer}, nil |
||||
} |
||||
|
||||
// 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) { |
||||
// Append plaintextBytes to existing buffered bytes
|
||||
n, err = aw.buffer.Write(plaintextBytes) |
||||
if err != nil { |
||||
return n, err |
||||
} |
||||
// Encrypt and write chunks
|
||||
for aw.buffer.Len() >= aw.chunkSize { |
||||
plainChunk := aw.buffer.Next(aw.chunkSize) |
||||
encryptedChunk, err := aw.sealChunk(plainChunk) |
||||
if err != nil { |
||||
return n, err |
||||
} |
||||
_, err = aw.writer.Write(encryptedChunk) |
||||
if err != nil { |
||||
return n, err |
||||
} |
||||
} |
||||
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.buffer.Len() > 0 || aw.bytesProcessed == 0 { |
||||
plainChunk := aw.buffer.Bytes() |
||||
lastEncryptedChunk, err := aw.sealChunk(plainChunk) |
||||
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, index, total number of encrypted octets).
|
||||
adata := append(aw.associatedData[:], aw.chunkIndex[:]...) |
||||
adata = append(adata, make([]byte, 8)...) |
||||
binary.BigEndian.PutUint64(adata[13:], uint64(aw.bytesProcessed)) |
||||
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 := append(aw.associatedData, aw.chunkIndex...) |
||||
nonce := aw.computeNextNonce() |
||||
encrypted := aw.aead.Seal(nil, nonce, data, adata) |
||||
aw.bytesProcessed += len(data) |
||||
if err := aw.aeadCrypter.incrementIndex(); err != nil { |
||||
return nil, err |
||||
} |
||||
return encrypted, nil |
||||
} |
||||
|
||||
// openChunk decrypts and checks integrity of an encrypted chunk, returning
|
||||
// the underlying plaintext and an error. It access peeked bytes from next
|
||||
// chunk, to identify the last chunk and decrypt/validate accordingly.
|
||||
func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) { |
||||
tagLen := ar.aead.Overhead() |
||||
// Restore carried bytes from last call
|
||||
chunkExtra := append(ar.peekedBytes, data...) |
||||
// 'chunk' contains encrypted bytes, followed by an authentication tag.
|
||||
chunk := chunkExtra[:len(chunkExtra)-tagLen] |
||||
ar.peekedBytes = chunkExtra[len(chunkExtra)-tagLen:] |
||||
adata := append(ar.associatedData, ar.chunkIndex...) |
||||
nonce := ar.computeNextNonce() |
||||
plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
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, index, and octets
|
||||
amountBytes := make([]byte, 8) |
||||
binary.BigEndian.PutUint64(amountBytes, uint64(ar.bytesProcessed)) |
||||
adata := append(ar.associatedData, ar.chunkIndex...) |
||||
adata = append(adata, amountBytes...) |
||||
nonce := ar.computeNextNonce() |
||||
_, err := ar.aead.Open(nil, nonce, tag, adata) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Associated data 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} |
||||
} |
||||
|
||||
// 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) { |
||||
nonce = make([]byte, len(wo.initialNonce)) |
||||
copy(nonce, wo.initialNonce) |
||||
offset := len(wo.initialNonce) - 8 |
||||
for i := 0; i < 8; i++ { |
||||
nonce[i+offset] ^= wo.chunkIndex[i] |
||||
} |
||||
return |
||||
} |
||||
|
||||
// incrementIndex perfoms 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") |
||||
} |
@ -0,0 +1,123 @@ |
||||
// 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" |
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"io" |
||||
"strconv" |
||||
) |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
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 1: |
||||
c.Body = flate.NewReader(r) |
||||
case 2: |
||||
c.Body, err = zlib.NewReader(r) |
||||
case 3: |
||||
c.Body = 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 |
||||
} |
@ -0,0 +1,167 @@ |
||||
// 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" |
||||
) |
||||
|
||||
// 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 |
||||
// S2KCount is only used for symmetric encryption. It
|
||||
// determines the strength of the passphrase stretching when
|
||||
// the said passphrase is hashed to produce a key. S2KCount
|
||||
// should be between 1024 and 65011712, inclusive. If Config
|
||||
// is nil or S2KCount is 0, the value 65536 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 |
||||
// 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 |
||||
// 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 |
||||
// V5Keys configures version 5 key generation. If false, this package still
|
||||
// supports version 5 keys, but produces version 4 keys.
|
||||
V5Keys 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 |
||||
} |
||||
|
||||
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() |
||||
} |
||||
return c.Time() |
||||
} |
||||
|
||||
// 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) PasswordHashIterations() int { |
||||
if c == nil || c.S2KCount == 0 { |
||||
return 0 |
||||
} |
||||
return c.S2KCount |
||||
} |
||||
|
||||
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) AEAD() *AEADConfig { |
||||
if c == nil { |
||||
return nil |
||||
} |
||||
return c.AEADConfig |
||||
} |
||||
|
||||
func (c *Config) SigningKey() uint64 { |
||||
if c == nil { |
||||
return 0 |
||||
} |
||||
return c.SigningKeyId |
||||
} |
@ -0,0 +1,282 @@ |
||||
// 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" |
||||
"crypto/rsa" |
||||
"encoding/binary" |
||||
"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" |
||||
) |
||||
|
||||
const encryptedKeyVersion = 3 |
||||
|
||||
// EncryptedKey represents a public-key encrypted session key. See RFC 4880,
|
||||
// section 5.1.
|
||||
type EncryptedKey struct { |
||||
KeyId uint64 |
||||
Algo PublicKeyAlgorithm |
||||
CipherFunc CipherFunction // only valid after a successful Decrypt
|
||||
Key []byte // only valid after a successful Decrypt
|
||||
|
||||
encryptedMPI1, encryptedMPI2 encoding.Field |
||||
} |
||||
|
||||
func (e *EncryptedKey) parse(r io.Reader) (err error) { |
||||
var buf [10]byte |
||||
_, err = readFull(r, buf[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if buf[0] != encryptedKeyVersion { |
||||
return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) |
||||
} |
||||
e.KeyId = binary.BigEndian.Uint64(buf[1:9]) |
||||
e.Algo = PublicKeyAlgorithm(buf[9]) |
||||
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 |
||||
} |
||||
} |
||||
_, err = consumeAll(r) |
||||
return |
||||
} |
||||
|
||||
func checksumKeyMaterial(key []byte) uint16 { |
||||
var checksum uint16 |
||||
for _, v := range key { |
||||
checksum += uint16(v) |
||||
} |
||||
return checksum |
||||
} |
||||
|
||||
// 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.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.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() |
||||
b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, priv.PublicKey.Fingerprint[:]) |
||||
default: |
||||
err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) |
||||
} |
||||
|
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
e.CipherFunc = CipherFunction(b[0]) |
||||
e.Key = b[1 : len(b)-2] |
||||
expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) |
||||
checksum := checksumKeyMaterial(e.Key) |
||||
if checksum != expectedChecksum { |
||||
return errors.StructuralError("EncryptedKey checksum incorrect") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Serialize writes the encrypted key packet, e, to w.
|
||||
func (e *EncryptedKey) Serialize(w io.Writer) error { |
||||
var mpiLen int |
||||
switch e.Algo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: |
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) |
||||
case PubKeyAlgoElGamal: |
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) |
||||
case PubKeyAlgoECDH: |
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) |
||||
default: |
||||
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo))) |
||||
} |
||||
|
||||
err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
w.Write([]byte{encryptedKeyVersion}) |
||||
binary.Write(w, binary.BigEndian, e.KeyId) |
||||
w.Write([]byte{byte(e.Algo)}) |
||||
|
||||
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 |
||||
default: |
||||
panic("internal error") |
||||
} |
||||
} |
||||
|
||||
// SerializeEncryptedKey serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { |
||||
var buf [10]byte |
||||
buf[0] = encryptedKeyVersion |
||||
binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) |
||||
buf[9] = byte(pub.PubKeyAlgo) |
||||
|
||||
keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) |
||||
keyBlock[0] = byte(cipherFunc) |
||||
copy(keyBlock[1:], key) |
||||
checksum := checksumKeyMaterial(key) |
||||
keyBlock[1+len(key)] = byte(checksum >> 8) |
||||
keyBlock[1+len(key)+1] = byte(checksum) |
||||
|
||||
switch pub.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: |
||||
return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) |
||||
case PubKeyAlgoElGamal: |
||||
return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) |
||||
case PubKeyAlgoECDH: |
||||
return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint) |
||||
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))) |
||||
} |
||||
|
||||
func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]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 := 10 /* 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 [10]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 := 10 /* 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 [10]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 := 10 /* 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 |
||||
} |
@ -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] = 't' |
||||
if isBinary { |
||||
buf[0] = 'b' |
||||
} |
||||
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 |
||||
} |
@ -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++ |
||||
} |
||||
} |
@ -0,0 +1,73 @@ |
||||
// 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" |
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k" |
||||
"io" |
||||
"strconv" |
||||
) |
||||
|
||||
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
|
||||
// section 5.4.
|
||||
type OnePassSignature struct { |
||||
SigType SignatureType |
||||
Hash crypto.Hash |
||||
PubKeyAlgo PublicKeyAlgorithm |
||||
KeyId uint64 |
||||
IsLast bool |
||||
} |
||||
|
||||
const onePassSignatureVersion = 3 |
||||
|
||||
func (ops *OnePassSignature) parse(r io.Reader) (err error) { |
||||
var buf [13]byte |
||||
|
||||
_, err = readFull(r, buf[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if buf[0] != onePassSignatureVersion { |
||||
err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) |
||||
} |
||||
|
||||
var ok bool |
||||
ops.Hash, ok = s2k.HashIdToHash(buf[2]) |
||||
if !ok { |
||||
return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) |
||||
} |
||||
|
||||
ops.SigType = SignatureType(buf[1]) |
||||
ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) |
||||
ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) |
||||
ops.IsLast = buf[12] != 0 |
||||
return |
||||
} |
||||
|
||||
// Serialize marshals the given OnePassSignature to w.
|
||||
func (ops *OnePassSignature) Serialize(w io.Writer) error { |
||||
var buf [13]byte |
||||
buf[0] = onePassSignatureVersion |
||||
buf[1] = uint8(ops.SigType) |
||||
var ok bool |
||||
buf[2], ok = s2k.HashToHashId(ops.Hash) |
||||
if !ok { |
||||
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) |
||||
} |
||||
buf[3] = uint8(ops.PubKeyAlgo) |
||||
binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) |
||||
if ops.IsLast { |
||||
buf[12] = 1 |
||||
} |
||||
|
||||
if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { |
||||
return err |
||||
} |
||||
_, err := w.Write(buf[:]) |
||||
return err |
||||
} |
@ -0,0 +1,162 @@ |
||||
// 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" |
||||
"io/ioutil" |
||||
|
||||
"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 = ioutil.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 |
||||
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 |
||||
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 |
||||
} |
||||
subLen = uint32(contents[0]) |
||||
contents = contents[1:] |
||||
case contents[0] < 255: |
||||
subHeaderLen = 3 // 2 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen { |
||||
goto Truncated |
||||
} |
||||
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 |
||||
} |
||||
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.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) |
||||
n := serializeSubpacketLength(buf, len(osp.Contents)+1) |
||||
buf[n] = osp.SubType |
||||
if _, err = w.Write(buf[:n+1]); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(osp.Contents) |
||||
return |
||||
} |
@ -0,0 +1,522 @@ |
||||
// 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 |
||||
packetTypeLiteralData packetType = 11 |
||||
packetTypeUserId packetType = 13 |
||||
packetTypePublicSubkey packetType = 14 |
||||
packetTypeUserAttribute packetType = 17 |
||||
packetTypeSymmetricallyEncryptedMDC packetType = 18 |
||||
packetTypeAEADEncrypted packetType = 20 |
||||
) |
||||
|
||||
// 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, _, 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: |
||||
err = errors.UnsupportedError("Symmetrically encrypted packets without MDC are not supported") |
||||
case packetTypeLiteralData: |
||||
p = new(LiteralData) |
||||
case packetTypeUserId: |
||||
p = new(UserId) |
||||
case packetTypeUserAttribute: |
||||
p = new(UserAttribute) |
||||
case packetTypeSymmetricallyEncryptedMDC: |
||||
se := new(SymmetricallyEncrypted) |
||||
se.MDC = true |
||||
p = se |
||||
case packetTypeAEADEncrypted: |
||||
p = new(AEADEncrypted) |
||||
default: |
||||
err = errors.UnknownPacketTypeError(tag) |
||||
} |
||||
if p != nil { |
||||
err = p.parse(contents) |
||||
} |
||||
if err != nil { |
||||
consumeAll(contents) |
||||
} |
||||
return |
||||
} |
||||
|
||||
// SignatureType represents the different semantic meanings of an OpenPGP
|
||||
// signature. See RFC 4880, section 5.2.1.
|
||||
type SignatureType uint8 |
||||
|
||||
const ( |
||||
SigTypeBinary SignatureType = 0x00 |
||||
SigTypeText = 0x01 |
||||
SigTypeGenericCert = 0x10 |
||||
SigTypePersonaCert = 0x11 |
||||
SigTypeCasualCert = 0x12 |
||||
SigTypePositiveCert = 0x13 |
||||
SigTypeSubkeyBinding = 0x18 |
||||
SigTypePrimaryKeyBinding = 0x19 |
||||
SigTypeDirectSignature = 0x1F |
||||
SigTypeKeyRevocation = 0x20 |
||||
SigTypeSubkeyRevocation = 0x28 |
||||
) |
||||
|
||||
// PublicKeyAlgorithm represents the different public key system specified for
|
||||
// OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12
|
||||
type PublicKeyAlgorithm uint8 |
||||
|
||||
const ( |
||||
PubKeyAlgoRSA PublicKeyAlgorithm = 1 |
||||
PubKeyAlgoElGamal PublicKeyAlgorithm = 16 |
||||
PubKeyAlgoDSA PublicKeyAlgorithm = 17 |
||||
// RFC 6637, Section 5.
|
||||
PubKeyAlgoECDH PublicKeyAlgorithm = 18 |
||||
PubKeyAlgoECDSA PublicKeyAlgorithm = 19 |
||||
// https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt
|
||||
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22 |
||||
|
||||
// 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: |
||||
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: |
||||
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() |
||||
} |
||||
|
||||
// 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.
|
||||
type AEADMode algorithm.AEADMode |
||||
|
||||
const ( |
||||
AEADModeEAX AEADMode = 1 |
||||
AEADModeOCB AEADMode = 2 |
||||
AEADModeExperimentalGCM AEADMode = 100 |
||||
) |
||||
|
||||
func (mode AEADMode) NonceLength() int { |
||||
return algorithm.AEADMode(mode).NonceLength() |
||||
} |
||||
|
||||
func (mode AEADMode) TagLength() int { |
||||
return algorithm.AEADMode(mode).TagLength() |
||||
} |
||||
|
||||
// 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 |
||||
) |
@ -0,0 +1,780 @@ |
||||
// 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/cipher" |
||||
"crypto/dsa" |
||||
"crypto/ecdsa" |
||||
"crypto/elliptic" |
||||
"crypto/rand" |
||||
"crypto/rsa" |
||||
"crypto/sha1" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"math/big" |
||||
"strconv" |
||||
"time" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc" |
||||
"golang.org/x/crypto/curve25519" |
||||
|
||||
"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/s2k" |
||||
"golang.org/x/crypto/ed25519" |
||||
) |
||||
|
||||
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
||||
// section 5.5.3.
|
||||
type PrivateKey struct { |
||||
PublicKey |
||||
Encrypted bool // if true then the private key is unavailable until Decrypt has been called.
|
||||
encryptedData []byte |
||||
cipher CipherFunction |
||||
s2k func(out, in []byte) |
||||
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or
|
||||
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
|
||||
PrivateKey interface{} |
||||
sha1Checksum bool |
||||
iv []byte |
||||
|
||||
// Type of encryption of the S2K packet
|
||||
// Allowed values are 0 (Not encrypted), 254 (SHA1), or
|
||||
// 255 (2-byte checksum)
|
||||
s2kType S2KType |
||||
// Full parameters of the S2K packet
|
||||
s2kParams *s2k.Params |
||||
} |
||||
|
||||
//S2KType s2k packet type
|
||||
type S2KType uint8 |
||||
|
||||
const ( |
||||
// S2KNON unencrypt
|
||||
S2KNON S2KType = 0 |
||||
// S2KSHA1 sha1 sum check
|
||||
S2KSHA1 S2KType = 254 |
||||
// S2KCHECKSUM sum check
|
||||
S2KCHECKSUM S2KType = 255 |
||||
) |
||||
|
||||
func NewRSAPrivateKey(creationTime time.Time, priv *rsa.PrivateKey) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey) |
||||
pk.PrivateKey = priv |
||||
return pk |
||||
} |
||||
|
||||
func NewDSAPrivateKey(creationTime time.Time, priv *dsa.PrivateKey) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
pk.PublicKey = *NewDSAPublicKey(creationTime, &priv.PublicKey) |
||||
pk.PrivateKey = priv |
||||
return pk |
||||
} |
||||
|
||||
func NewElGamalPrivateKey(creationTime time.Time, priv *elgamal.PrivateKey) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey) |
||||
pk.PrivateKey = priv |
||||
return pk |
||||
} |
||||
|
||||
func NewECDSAPrivateKey(creationTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
pk.PublicKey = *NewECDSAPublicKey(creationTime, &priv.PublicKey) |
||||
pk.PrivateKey = priv |
||||
return pk |
||||
} |
||||
|
||||
func NewEdDSAPrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
pub := priv.Public().(ed25519.PublicKey) |
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pub) |
||||
pk.PrivateKey = priv |
||||
return pk |
||||
} |
||||
|
||||
func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey) |
||||
pk.PrivateKey = priv |
||||
return pk |
||||
} |
||||
|
||||
// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that
|
||||
// implements RSA, ECDSA or EdDSA.
|
||||
func NewSignerPrivateKey(creationTime time.Time, signer crypto.Signer) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
// In general, the public Keys should be used as pointers. We still
|
||||
// type-switch on the values, for backwards-compatibility.
|
||||
switch pubkey := signer.Public().(type) { |
||||
case *rsa.PublicKey: |
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, pubkey) |
||||
case rsa.PublicKey: |
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey) |
||||
case *ecdsa.PublicKey: |
||||
pk.PublicKey = *NewECDSAPublicKey(creationTime, pubkey) |
||||
case ecdsa.PublicKey: |
||||
pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey) |
||||
case *ed25519.PublicKey: |
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, pubkey) |
||||
case ed25519.PublicKey: |
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey) |
||||
default: |
||||
panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey") |
||||
} |
||||
pk.PrivateKey = signer |
||||
return pk |
||||
} |
||||
|
||||
// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey.
|
||||
func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey { |
||||
pk := new(PrivateKey) |
||||
switch priv := decrypter.(type) { |
||||
case *rsa.PrivateKey: |
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey) |
||||
case *elgamal.PrivateKey: |
||||
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey) |
||||
case *ecdh.PrivateKey: |
||||
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey) |
||||
default: |
||||
panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey") |
||||
} |
||||
pk.PrivateKey = decrypter |
||||
return pk |
||||
} |
||||
|
||||
func (pk *PrivateKey) parse(r io.Reader) (err error) { |
||||
err = (&pk.PublicKey).parse(r) |
||||
if err != nil { |
||||
return |
||||
} |
||||
v5 := pk.PublicKey.Version == 5 |
||||
|
||||
var buf [1]byte |
||||
_, err = readFull(r, buf[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
pk.s2kType = S2KType(buf[0]) |
||||
var optCount [1]byte |
||||
if v5 { |
||||
if _, err = readFull(r, optCount[:]); err != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
switch pk.s2kType { |
||||
case S2KNON: |
||||
pk.s2k = nil |
||||
pk.Encrypted = false |
||||
case S2KSHA1, S2KCHECKSUM: |
||||
if v5 && pk.s2kType == S2KCHECKSUM { |
||||
return errors.StructuralError("wrong s2k identifier for version 5") |
||||
} |
||||
_, err = readFull(r, buf[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
pk.cipher = CipherFunction(buf[0]) |
||||
pk.s2kParams, err = s2k.ParseIntoParams(r) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if pk.s2kParams.Dummy() { |
||||
return |
||||
} |
||||
pk.s2k, err = pk.s2kParams.Function() |
||||
if err != nil { |
||||
return |
||||
} |
||||
pk.Encrypted = true |
||||
if pk.s2kType == S2KSHA1 { |
||||
pk.sha1Checksum = true |
||||
} |
||||
default: |
||||
return errors.UnsupportedError("deprecated s2k function in private key") |
||||
} |
||||
|
||||
if pk.Encrypted { |
||||
blockSize := pk.cipher.blockSize() |
||||
if blockSize == 0 { |
||||
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) |
||||
} |
||||
pk.iv = make([]byte, blockSize) |
||||
_, err = readFull(r, pk.iv) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
var privateKeyData []byte |
||||
if v5 { |
||||
var n [4]byte /* secret material four octet count */ |
||||
_, err = readFull(r, n[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
count := uint32(uint32(n[0])<<24 | uint32(n[1])<<16 | uint32(n[2])<<8 | uint32(n[3])) |
||||
if !pk.Encrypted { |
||||
count = count + 2 /* two octet checksum */ |
||||
} |
||||
privateKeyData = make([]byte, count) |
||||
_, err = readFull(r, privateKeyData) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} else { |
||||
privateKeyData, err = ioutil.ReadAll(r) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
if !pk.Encrypted { |
||||
return pk.parsePrivateKey(privateKeyData) |
||||
} |
||||
|
||||
pk.encryptedData = privateKeyData |
||||
return |
||||
} |
||||
|
||||
// Dummy returns true if the private key is a dummy key. This is a GNU extension.
|
||||
func (pk *PrivateKey) Dummy() bool { |
||||
return pk.s2kParams.Dummy() |
||||
} |
||||
|
||||
func mod64kHash(d []byte) uint16 { |
||||
var h uint16 |
||||
for _, b := range d { |
||||
h += uint16(b) |
||||
} |
||||
return h |
||||
} |
||||
|
||||
func (pk *PrivateKey) Serialize(w io.Writer) (err error) { |
||||
contents := bytes.NewBuffer(nil) |
||||
err = pk.PublicKey.serializeWithoutHeaders(contents) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if _, err = contents.Write([]byte{uint8(pk.s2kType)}); err != nil { |
||||
return |
||||
} |
||||
|
||||
optional := bytes.NewBuffer(nil) |
||||
if pk.Encrypted || pk.Dummy() { |
||||
optional.Write([]byte{uint8(pk.cipher)}) |
||||
if err := pk.s2kParams.Serialize(optional); err != nil { |
||||
return err |
||||
} |
||||
if pk.Encrypted { |
||||
optional.Write(pk.iv) |
||||
} |
||||
} |
||||
if pk.Version == 5 { |
||||
contents.Write([]byte{uint8(optional.Len())}) |
||||
} |
||||
io.Copy(contents, optional) |
||||
|
||||
if !pk.Dummy() { |
||||
l := 0 |
||||
var priv []byte |
||||
if !pk.Encrypted { |
||||
buf := bytes.NewBuffer(nil) |
||||
err = pk.serializePrivateKey(buf) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
l = buf.Len() |
||||
if pk.sha1Checksum { |
||||
h := sha1.New() |
||||
h.Write(buf.Bytes()) |
||||
buf.Write(h.Sum(nil)) |
||||
} else { |
||||
checksum := mod64kHash(buf.Bytes()) |
||||
buf.Write([]byte{byte(checksum >> 8), byte(checksum)}) |
||||
} |
||||
priv = buf.Bytes() |
||||
} else { |
||||
priv, l = pk.encryptedData, len(pk.encryptedData) |
||||
} |
||||
|
||||
if pk.Version == 5 { |
||||
contents.Write([]byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)}) |
||||
} |
||||
contents.Write(priv) |
||||
} |
||||
|
||||
ptype := packetTypePrivateKey |
||||
if pk.IsSubkey { |
||||
ptype = packetTypePrivateSubkey |
||||
} |
||||
err = serializeHeader(w, ptype, contents.Len()) |
||||
if err != nil { |
||||
return |
||||
} |
||||
_, err = io.Copy(w, contents) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return |
||||
} |
||||
|
||||
func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error { |
||||
if _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()); err != nil { |
||||
return err |
||||
} |
||||
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[1]).EncodedBytes()); err != nil { |
||||
return err |
||||
} |
||||
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[0]).EncodedBytes()); err != nil { |
||||
return err |
||||
} |
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.Precomputed.Qinv).EncodedBytes()) |
||||
return err |
||||
} |
||||
|
||||
func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error { |
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes()) |
||||
return err |
||||
} |
||||
|
||||
func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error { |
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes()) |
||||
return err |
||||
} |
||||
|
||||
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error { |
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()) |
||||
return err |
||||
} |
||||
|
||||
func serializeEdDSAPrivateKey(w io.Writer, priv *ed25519.PrivateKey) error { |
||||
keySize := ed25519.PrivateKeySize - ed25519.PublicKeySize |
||||
_, err := w.Write(encoding.NewMPI((*priv)[:keySize]).EncodedBytes()) |
||||
return err |
||||
} |
||||
|
||||
func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error { |
||||
_, err := w.Write(encoding.NewMPI(priv.D).EncodedBytes()) |
||||
return err |
||||
} |
||||
|
||||
// Decrypt decrypts an encrypted private key using a passphrase.
|
||||
func (pk *PrivateKey) Decrypt(passphrase []byte) error { |
||||
if pk.Dummy() { |
||||
return errors.ErrDummyPrivateKey("dummy key found") |
||||
} |
||||
if !pk.Encrypted { |
||||
return nil |
||||
} |
||||
|
||||
key := make([]byte, pk.cipher.KeySize()) |
||||
pk.s2k(key, passphrase) |
||||
block := pk.cipher.new(key) |
||||
cfb := cipher.NewCFBDecrypter(block, pk.iv) |
||||
|
||||
data := make([]byte, len(pk.encryptedData)) |
||||
cfb.XORKeyStream(data, pk.encryptedData) |
||||
|
||||
if pk.sha1Checksum { |
||||
if len(data) < sha1.Size { |
||||
return errors.StructuralError("truncated private key data") |
||||
} |
||||
h := sha1.New() |
||||
h.Write(data[:len(data)-sha1.Size]) |
||||
sum := h.Sum(nil) |
||||
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { |
||||
return errors.StructuralError("private key checksum failure") |
||||
} |
||||
data = data[:len(data)-sha1.Size] |
||||
} else { |
||||
if len(data) < 2 { |
||||
return errors.StructuralError("truncated private key data") |
||||
} |
||||
var sum uint16 |
||||
for i := 0; i < len(data)-2; i++ { |
||||
sum += uint16(data[i]) |
||||
} |
||||
if data[len(data)-2] != uint8(sum>>8) || |
||||
data[len(data)-1] != uint8(sum) { |
||||
return errors.StructuralError("private key checksum failure") |
||||
} |
||||
data = data[:len(data)-2] |
||||
} |
||||
|
||||
err := pk.parsePrivateKey(data) |
||||
if _, ok := err.(errors.KeyInvalidError); ok { |
||||
return errors.KeyInvalidError("invalid key parameters") |
||||
} |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Mark key as unencrypted
|
||||
pk.s2kType = S2KNON |
||||
pk.s2k = nil |
||||
pk.Encrypted = false |
||||
pk.encryptedData = nil |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Encrypt encrypts an unencrypted private key using a passphrase.
|
||||
func (pk *PrivateKey) Encrypt(passphrase []byte) error { |
||||
priv := bytes.NewBuffer(nil) |
||||
err := pk.serializePrivateKey(priv) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
//Default config of private key encryption
|
||||
pk.cipher = CipherAES256 |
||||
s2kConfig := &s2k.Config{ |
||||
S2KMode: 3, //Iterated
|
||||
S2KCount: 65536, |
||||
Hash: crypto.SHA256, |
||||
} |
||||
|
||||
pk.s2kParams, err = s2k.Generate(rand.Reader, s2kConfig) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
privateKeyBytes := priv.Bytes() |
||||
key := make([]byte, pk.cipher.KeySize()) |
||||
|
||||
pk.sha1Checksum = true |
||||
pk.s2k, err = pk.s2kParams.Function() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
pk.s2k(key, passphrase) |
||||
block := pk.cipher.new(key) |
||||
pk.iv = make([]byte, pk.cipher.blockSize()) |
||||
_, err = rand.Read(pk.iv) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
cfb := cipher.NewCFBEncrypter(block, pk.iv) |
||||
|
||||
if pk.sha1Checksum { |
||||
pk.s2kType = S2KSHA1 |
||||
h := sha1.New() |
||||
h.Write(privateKeyBytes) |
||||
sum := h.Sum(nil) |
||||
privateKeyBytes = append(privateKeyBytes, sum...) |
||||
} else { |
||||
pk.s2kType = S2KCHECKSUM |
||||
var sum uint16 |
||||
for _, b := range privateKeyBytes { |
||||
sum += uint16(b) |
||||
} |
||||
priv.Write([]byte{uint8(sum >> 8), uint8(sum)}) |
||||
} |
||||
|
||||
pk.encryptedData = make([]byte, len(privateKeyBytes)) |
||||
cfb.XORKeyStream(pk.encryptedData, privateKeyBytes) |
||||
pk.Encrypted = true |
||||
pk.PrivateKey = nil |
||||
return err |
||||
} |
||||
|
||||
func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) { |
||||
switch priv := pk.PrivateKey.(type) { |
||||
case *rsa.PrivateKey: |
||||
err = serializeRSAPrivateKey(w, priv) |
||||
case *dsa.PrivateKey: |
||||
err = serializeDSAPrivateKey(w, priv) |
||||
case *elgamal.PrivateKey: |
||||
err = serializeElGamalPrivateKey(w, priv) |
||||
case *ecdsa.PrivateKey: |
||||
err = serializeECDSAPrivateKey(w, priv) |
||||
case *ed25519.PrivateKey: |
||||
err = serializeEdDSAPrivateKey(w, priv) |
||||
case *ecdh.PrivateKey: |
||||
err = serializeECDHPrivateKey(w, priv) |
||||
default: |
||||
err = errors.InvalidArgumentError("unknown private key type") |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) { |
||||
switch pk.PublicKey.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: |
||||
return pk.parseRSAPrivateKey(data) |
||||
case PubKeyAlgoDSA: |
||||
return pk.parseDSAPrivateKey(data) |
||||
case PubKeyAlgoElGamal: |
||||
return pk.parseElGamalPrivateKey(data) |
||||
case PubKeyAlgoECDSA: |
||||
return pk.parseECDSAPrivateKey(data) |
||||
case PubKeyAlgoECDH: |
||||
return pk.parseECDHPrivateKey(data) |
||||
case PubKeyAlgoEdDSA: |
||||
return pk.parseEdDSAPrivateKey(data) |
||||
} |
||||
panic("impossible") |
||||
} |
||||
|
||||
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { |
||||
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) |
||||
rsaPriv := new(rsa.PrivateKey) |
||||
rsaPriv.PublicKey = *rsaPub |
||||
|
||||
buf := bytes.NewBuffer(data) |
||||
d := new(encoding.MPI) |
||||
if _, err := d.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
p := new(encoding.MPI) |
||||
if _, err := p.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
q := new(encoding.MPI) |
||||
if _, err := q.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
rsaPriv.D = new(big.Int).SetBytes(d.Bytes()) |
||||
rsaPriv.Primes = make([]*big.Int, 2) |
||||
rsaPriv.Primes[0] = new(big.Int).SetBytes(p.Bytes()) |
||||
rsaPriv.Primes[1] = new(big.Int).SetBytes(q.Bytes()) |
||||
if err := rsaPriv.Validate(); err != nil { |
||||
return errors.KeyInvalidError(err.Error()) |
||||
} |
||||
rsaPriv.Precompute() |
||||
pk.PrivateKey = rsaPriv |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) { |
||||
dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) |
||||
dsaPriv := new(dsa.PrivateKey) |
||||
dsaPriv.PublicKey = *dsaPub |
||||
|
||||
buf := bytes.NewBuffer(data) |
||||
x := new(encoding.MPI) |
||||
if _, err := x.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
dsaPriv.X = new(big.Int).SetBytes(x.Bytes()) |
||||
if err := validateDSAParameters(dsaPriv); err != nil { |
||||
return err |
||||
} |
||||
pk.PrivateKey = dsaPriv |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) { |
||||
pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) |
||||
priv := new(elgamal.PrivateKey) |
||||
priv.PublicKey = *pub |
||||
|
||||
buf := bytes.NewBuffer(data) |
||||
x := new(encoding.MPI) |
||||
if _, err := x.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
priv.X = new(big.Int).SetBytes(x.Bytes()) |
||||
if err := validateElGamalParameters(priv); err != nil { |
||||
return err |
||||
} |
||||
pk.PrivateKey = priv |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) { |
||||
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey) |
||||
ecdsaPriv := new(ecdsa.PrivateKey) |
||||
ecdsaPriv.PublicKey = *ecdsaPub |
||||
|
||||
buf := bytes.NewBuffer(data) |
||||
d := new(encoding.MPI) |
||||
if _, err := d.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
ecdsaPriv.D = new(big.Int).SetBytes(d.Bytes()) |
||||
if err := validateECDSAParameters(ecdsaPriv); err != nil { |
||||
return err |
||||
} |
||||
pk.PrivateKey = ecdsaPriv |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) { |
||||
ecdhPub := pk.PublicKey.PublicKey.(*ecdh.PublicKey) |
||||
ecdhPriv := new(ecdh.PrivateKey) |
||||
ecdhPriv.PublicKey = *ecdhPub |
||||
|
||||
buf := bytes.NewBuffer(data) |
||||
d := new(encoding.MPI) |
||||
if _, err := d.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
ecdhPriv.D = d.Bytes() |
||||
if err := validateECDHParameters(ecdhPriv); err != nil { |
||||
return err |
||||
} |
||||
pk.PrivateKey = ecdhPriv |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) { |
||||
eddsaPub := pk.PublicKey.PublicKey.(*ed25519.PublicKey) |
||||
eddsaPriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize) |
||||
|
||||
buf := bytes.NewBuffer(data) |
||||
d := new(encoding.MPI) |
||||
if _, err := d.ReadFrom(buf); err != nil { |
||||
return err |
||||
} |
||||
|
||||
priv := d.Bytes() |
||||
copy(eddsaPriv[32-len(priv):32], priv) |
||||
copy(eddsaPriv[32:], (*eddsaPub)[:]) |
||||
if err := validateEdDSAParameters(&eddsaPriv); err != nil { |
||||
return err |
||||
} |
||||
pk.PrivateKey = &eddsaPriv |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func validateECDSAParameters(priv *ecdsa.PrivateKey) error { |
||||
return validateCommonECC(priv.Curve, priv.D.Bytes(), priv.X, priv.Y) |
||||
} |
||||
|
||||
func validateECDHParameters(priv *ecdh.PrivateKey) error { |
||||
if priv.CurveType != ecc.Curve25519 { |
||||
return validateCommonECC(priv.Curve, priv.D, priv.X, priv.Y) |
||||
} |
||||
// Handle Curve25519
|
||||
Q := priv.X.Bytes()[1:] |
||||
var d [32]byte |
||||
// Copy reversed d
|
||||
l := len(priv.D) |
||||
for i := 0; i < l; i++ { |
||||
d[i] = priv.D[l-i-1] |
||||
} |
||||
var expectedQ [32]byte |
||||
curve25519.ScalarBaseMult(&expectedQ, &d) |
||||
if !bytes.Equal(Q, expectedQ[:]) { |
||||
return errors.KeyInvalidError("ECDH curve25519: invalid point") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func validateCommonECC(curve elliptic.Curve, d []byte, X, Y *big.Int) error { |
||||
// the public point should not be at infinity (0,0)
|
||||
zero := new(big.Int) |
||||
if X.Cmp(zero) == 0 && Y.Cmp(zero) == 0 { |
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): infinity point", curve.Params().Name)) |
||||
} |
||||
// re-derive the public point Q' = (X,Y) = dG
|
||||
// to compare to declared Q in public key
|
||||
expectedX, expectedY := curve.ScalarBaseMult(d) |
||||
if X.Cmp(expectedX) != 0 || Y.Cmp(expectedY) != 0 { |
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", curve.Params().Name)) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func validateEdDSAParameters(priv *ed25519.PrivateKey) error { |
||||
// In EdDSA, the serialized public point is stored as part of private key (together with the seed),
|
||||
// hence we can re-derive the key from the seed
|
||||
seed := priv.Seed() |
||||
expectedPriv := ed25519.NewKeyFromSeed(seed) |
||||
if !bytes.Equal(*priv, expectedPriv) { |
||||
return errors.KeyInvalidError("eddsa: invalid point") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func validateDSAParameters(priv *dsa.PrivateKey) error { |
||||
p := priv.P // group prime
|
||||
q := priv.Q // subgroup order
|
||||
g := priv.G // g has order q mod p
|
||||
x := priv.X // secret
|
||||
y := priv.Y // y == g**x mod p
|
||||
one := big.NewInt(1) |
||||
// expect g, y >= 2 and g < p
|
||||
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 { |
||||
return errors.KeyInvalidError("dsa: invalid group") |
||||
} |
||||
// expect p > q
|
||||
if p.Cmp(q) <= 0 { |
||||
return errors.KeyInvalidError("dsa: invalid group prime") |
||||
} |
||||
// q should be large enough and divide p-1
|
||||
pSub1 := new(big.Int).Sub(p, one) |
||||
if q.BitLen() < 150 || new(big.Int).Mod(pSub1, q).Cmp(big.NewInt(0)) != 0 { |
||||
return errors.KeyInvalidError("dsa: invalid order") |
||||
} |
||||
// confirm that g has order q mod p
|
||||
if !q.ProbablyPrime(32) || new(big.Int).Exp(g, q, p).Cmp(one) != 0 { |
||||
return errors.KeyInvalidError("dsa: invalid order") |
||||
} |
||||
// check y
|
||||
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 { |
||||
return errors.KeyInvalidError("dsa: mismatching values") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func validateElGamalParameters(priv *elgamal.PrivateKey) error { |
||||
p := priv.P // group prime
|
||||
g := priv.G // g has order p-1 mod p
|
||||
x := priv.X // secret
|
||||
y := priv.Y // y == g**x mod p
|
||||
one := big.NewInt(1) |
||||
// Expect g, y >= 2 and g < p
|
||||
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 { |
||||
return errors.KeyInvalidError("elgamal: invalid group") |
||||
} |
||||
if p.BitLen() < 1024 { |
||||
return errors.KeyInvalidError("elgamal: group order too small") |
||||
} |
||||
pSub1 := new(big.Int).Sub(p, one) |
||||
if new(big.Int).Exp(g, pSub1, p).Cmp(one) != 0 { |
||||
return errors.KeyInvalidError("elgamal: invalid group") |
||||
} |
||||
// Since p-1 is not prime, g might have a smaller order that divides p-1.
|
||||
// We cannot confirm the exact order of g, but we make sure it is not too small.
|
||||
gExpI := new(big.Int).Set(g) |
||||
i := 1 |
||||
threshold := 2 << 17 // we want order > threshold
|
||||
for i < threshold { |
||||
i++ // we check every order to make sure key validation is not easily bypassed by guessing y'
|
||||
gExpI.Mod(new(big.Int).Mul(gExpI, g), p) |
||||
if gExpI.Cmp(one) == 0 { |
||||
return errors.KeyInvalidError("elgamal: order too small") |
||||
} |
||||
} |
||||
// Check y
|
||||
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 { |
||||
return errors.KeyInvalidError("elgamal: mismatching values") |
||||
} |
||||
|
||||
return nil |
||||
} |
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go
generated
vendored
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go
generated
vendored
@ -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" |
@ -0,0 +1,825 @@ |
||||
// 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" |
||||
"crypto/dsa" |
||||
"crypto/ecdsa" |
||||
"crypto/elliptic" |
||||
"crypto/rsa" |
||||
"crypto/sha1" |
||||
"crypto/sha256" |
||||
_ "crypto/sha512" |
||||
"encoding/binary" |
||||
"fmt" |
||||
"hash" |
||||
"io" |
||||
"math/big" |
||||
"strconv" |
||||
"time" |
||||
|
||||
"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/algorithm" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding" |
||||
"golang.org/x/crypto/ed25519" |
||||
) |
||||
|
||||
type kdfHashFunction byte |
||||
type kdfAlgorithm byte |
||||
|
||||
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
|
||||
type PublicKey struct { |
||||
Version int |
||||
CreationTime time.Time |
||||
PubKeyAlgo PublicKeyAlgorithm |
||||
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey
|
||||
Fingerprint []byte |
||||
KeyId uint64 |
||||
IsSubkey bool |
||||
|
||||
// RFC 4880 fields
|
||||
n, e, p, q, g, y encoding.Field |
||||
|
||||
// RFC 6637 fields
|
||||
// oid contains the OID byte sequence identifying the elliptic curve used
|
||||
oid encoding.Field |
||||
|
||||
// kdf stores key derivation function parameters
|
||||
// used for ECDH encryption. See RFC 6637, Section 9.
|
||||
kdf encoding.Field |
||||
} |
||||
|
||||
// UpgradeToV5 updates the version of the key to v5, and updates all necessary
|
||||
// fields.
|
||||
func (pk *PublicKey) UpgradeToV5() { |
||||
pk.Version = 5 |
||||
pk.setFingerprintAndKeyId() |
||||
} |
||||
|
||||
// signingKey provides a convenient abstraction over signature verification
|
||||
// for v3 and v4 public keys.
|
||||
type signingKey interface { |
||||
SerializeForHash(io.Writer) error |
||||
SerializeSignaturePrefix(io.Writer) |
||||
serializeWithoutHeaders(io.Writer) error |
||||
} |
||||
|
||||
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
|
||||
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey { |
||||
pk := &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoRSA, |
||||
PublicKey: pub, |
||||
n: new(encoding.MPI).SetBig(pub.N), |
||||
e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))), |
||||
} |
||||
|
||||
pk.setFingerprintAndKeyId() |
||||
return pk |
||||
} |
||||
|
||||
// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey.
|
||||
func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey { |
||||
pk := &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoDSA, |
||||
PublicKey: pub, |
||||
p: new(encoding.MPI).SetBig(pub.P), |
||||
q: new(encoding.MPI).SetBig(pub.Q), |
||||
g: new(encoding.MPI).SetBig(pub.G), |
||||
y: new(encoding.MPI).SetBig(pub.Y), |
||||
} |
||||
|
||||
pk.setFingerprintAndKeyId() |
||||
return pk |
||||
} |
||||
|
||||
// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey.
|
||||
func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey { |
||||
pk := &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoElGamal, |
||||
PublicKey: pub, |
||||
p: new(encoding.MPI).SetBig(pub.P), |
||||
g: new(encoding.MPI).SetBig(pub.G), |
||||
y: new(encoding.MPI).SetBig(pub.Y), |
||||
} |
||||
|
||||
pk.setFingerprintAndKeyId() |
||||
return pk |
||||
} |
||||
|
||||
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey { |
||||
pk := &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoECDSA, |
||||
PublicKey: pub, |
||||
p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)), |
||||
} |
||||
|
||||
curveInfo := ecc.FindByCurve(pub.Curve) |
||||
if curveInfo == nil { |
||||
panic("unknown elliptic curve") |
||||
} |
||||
pk.oid = curveInfo.Oid |
||||
pk.setFingerprintAndKeyId() |
||||
return pk |
||||
} |
||||
|
||||
func NewECDHPublicKey(creationTime time.Time, pub *ecdh.PublicKey) *PublicKey { |
||||
var pk *PublicKey |
||||
var curveInfo *ecc.CurveInfo |
||||
var kdf = encoding.NewOID([]byte{0x1, pub.Hash.Id(), pub.Cipher.Id()}) |
||||
if pub.CurveType == ecc.Curve25519 { |
||||
pk = &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoECDH, |
||||
PublicKey: pub, |
||||
p: encoding.NewMPI(pub.X.Bytes()), |
||||
kdf: kdf, |
||||
} |
||||
curveInfo = ecc.FindByName("Curve25519") |
||||
} else { |
||||
pk = &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoECDH, |
||||
PublicKey: pub, |
||||
p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)), |
||||
kdf: kdf, |
||||
} |
||||
curveInfo = ecc.FindByCurve(pub.Curve) |
||||
} |
||||
if curveInfo == nil { |
||||
panic("unknown elliptic curve") |
||||
} |
||||
pk.oid = curveInfo.Oid |
||||
pk.setFingerprintAndKeyId() |
||||
return pk |
||||
} |
||||
|
||||
func NewEdDSAPublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey { |
||||
curveInfo := ecc.FindByName("Ed25519") |
||||
pk := &PublicKey{ |
||||
Version: 4, |
||||
CreationTime: creationTime, |
||||
PubKeyAlgo: PubKeyAlgoEdDSA, |
||||
PublicKey: pub, |
||||
oid: curveInfo.Oid, |
||||
// Native point format, see draft-koch-eddsa-for-openpgp-04, Appendix B
|
||||
p: encoding.NewMPI(append([]byte{0x40}, *pub...)), |
||||
} |
||||
|
||||
pk.setFingerprintAndKeyId() |
||||
return pk |
||||
} |
||||
|
||||
func (pk *PublicKey) parse(r io.Reader) (err error) { |
||||
// RFC 4880, section 5.5.2
|
||||
var buf [6]byte |
||||
_, err = readFull(r, buf[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if buf[0] != 4 && buf[0] != 5 { |
||||
return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0]))) |
||||
} |
||||
|
||||
pk.Version = int(buf[0]) |
||||
if pk.Version == 5 { |
||||
var n [4]byte |
||||
_, err = readFull(r, n[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) |
||||
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) |
||||
switch pk.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: |
||||
err = pk.parseRSA(r) |
||||
case PubKeyAlgoDSA: |
||||
err = pk.parseDSA(r) |
||||
case PubKeyAlgoElGamal: |
||||
err = pk.parseElGamal(r) |
||||
case PubKeyAlgoECDSA: |
||||
err = pk.parseECDSA(r) |
||||
case PubKeyAlgoECDH: |
||||
err = pk.parseECDH(r) |
||||
case PubKeyAlgoEdDSA: |
||||
err = pk.parseEdDSA(r) |
||||
default: |
||||
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) |
||||
} |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
pk.setFingerprintAndKeyId() |
||||
return |
||||
} |
||||
|
||||
func (pk *PublicKey) setFingerprintAndKeyId() { |
||||
// RFC 4880, section 12.2
|
||||
if pk.Version == 5 { |
||||
fingerprint := sha256.New() |
||||
pk.SerializeForHash(fingerprint) |
||||
pk.Fingerprint = make([]byte, 32) |
||||
copy(pk.Fingerprint, fingerprint.Sum(nil)) |
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8]) |
||||
} else { |
||||
fingerprint := sha1.New() |
||||
pk.SerializeForHash(fingerprint) |
||||
pk.Fingerprint = make([]byte, 20) |
||||
copy(pk.Fingerprint, fingerprint.Sum(nil)) |
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) |
||||
} |
||||
} |
||||
|
||||
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseRSA(r io.Reader) (err error) { |
||||
pk.n = new(encoding.MPI) |
||||
if _, err = pk.n.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.e = new(encoding.MPI) |
||||
if _, err = pk.e.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
if len(pk.e.Bytes()) > 3 { |
||||
err = errors.UnsupportedError("large public exponent") |
||||
return |
||||
} |
||||
rsa := &rsa.PublicKey{ |
||||
N: new(big.Int).SetBytes(pk.n.Bytes()), |
||||
E: 0, |
||||
} |
||||
for i := 0; i < len(pk.e.Bytes()); i++ { |
||||
rsa.E <<= 8 |
||||
rsa.E |= int(pk.e.Bytes()[i]) |
||||
} |
||||
pk.PublicKey = rsa |
||||
return |
||||
} |
||||
|
||||
// parseDSA parses DSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseDSA(r io.Reader) (err error) { |
||||
pk.p = new(encoding.MPI) |
||||
if _, err = pk.p.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.q = new(encoding.MPI) |
||||
if _, err = pk.q.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.g = new(encoding.MPI) |
||||
if _, err = pk.g.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.y = new(encoding.MPI) |
||||
if _, err = pk.y.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
dsa := new(dsa.PublicKey) |
||||
dsa.P = new(big.Int).SetBytes(pk.p.Bytes()) |
||||
dsa.Q = new(big.Int).SetBytes(pk.q.Bytes()) |
||||
dsa.G = new(big.Int).SetBytes(pk.g.Bytes()) |
||||
dsa.Y = new(big.Int).SetBytes(pk.y.Bytes()) |
||||
pk.PublicKey = dsa |
||||
return |
||||
} |
||||
|
||||
// parseElGamal parses ElGamal public key material from the given Reader. See
|
||||
// RFC 4880, section 5.5.2.
|
||||
func (pk *PublicKey) parseElGamal(r io.Reader) (err error) { |
||||
pk.p = new(encoding.MPI) |
||||
if _, err = pk.p.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.g = new(encoding.MPI) |
||||
if _, err = pk.g.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.y = new(encoding.MPI) |
||||
if _, err = pk.y.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
elgamal := new(elgamal.PublicKey) |
||||
elgamal.P = new(big.Int).SetBytes(pk.p.Bytes()) |
||||
elgamal.G = new(big.Int).SetBytes(pk.g.Bytes()) |
||||
elgamal.Y = new(big.Int).SetBytes(pk.y.Bytes()) |
||||
pk.PublicKey = elgamal |
||||
return |
||||
} |
||||
|
||||
// parseECDSA parses ECDSA public key material from the given Reader. See
|
||||
// RFC 6637, Section 9.
|
||||
func (pk *PublicKey) parseECDSA(r io.Reader) (err error) { |
||||
pk.oid = new(encoding.OID) |
||||
if _, err = pk.oid.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.p = new(encoding.MPI) |
||||
if _, err = pk.p.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
var c elliptic.Curve |
||||
curveInfo := ecc.FindByOid(pk.oid) |
||||
if curveInfo == nil || curveInfo.SigAlgorithm != ecc.ECDSA { |
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) |
||||
} |
||||
c = curveInfo.Curve |
||||
x, y := elliptic.Unmarshal(c, pk.p.Bytes()) |
||||
if x == nil { |
||||
return errors.UnsupportedError("failed to parse EC point") |
||||
} |
||||
pk.PublicKey = &ecdsa.PublicKey{Curve: c, X: x, Y: y} |
||||
return |
||||
} |
||||
|
||||
// parseECDH parses ECDH public key material from the given Reader. See
|
||||
// RFC 6637, Section 9.
|
||||
func (pk *PublicKey) parseECDH(r io.Reader) (err error) { |
||||
pk.oid = new(encoding.OID) |
||||
if _, err = pk.oid.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.p = new(encoding.MPI) |
||||
if _, err = pk.p.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
pk.kdf = new(encoding.OID) |
||||
if _, err = pk.kdf.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
curveInfo := ecc.FindByOid(pk.oid) |
||||
if curveInfo == nil { |
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) |
||||
} |
||||
|
||||
c := curveInfo.Curve |
||||
cType := curveInfo.CurveType |
||||
|
||||
var x, y *big.Int |
||||
if cType == ecc.Curve25519 { |
||||
x = new(big.Int) |
||||
x.SetBytes(pk.p.Bytes()) |
||||
} else { |
||||
x, y = elliptic.Unmarshal(c, pk.p.Bytes()) |
||||
} |
||||
if x == nil { |
||||
return errors.UnsupportedError("failed to parse EC point") |
||||
} |
||||
|
||||
if kdfLen := len(pk.kdf.Bytes()); kdfLen < 3 { |
||||
return errors.UnsupportedError("unsupported ECDH KDF length: " + strconv.Itoa(kdfLen)) |
||||
} |
||||
if reserved := pk.kdf.Bytes()[0]; reserved != 0x01 { |
||||
return errors.UnsupportedError("unsupported KDF reserved field: " + strconv.Itoa(int(reserved))) |
||||
} |
||||
kdfHash, ok := algorithm.HashById[pk.kdf.Bytes()[1]] |
||||
if !ok { |
||||
return errors.UnsupportedError("unsupported ECDH KDF hash: " + strconv.Itoa(int(pk.kdf.Bytes()[1]))) |
||||
} |
||||
kdfCipher, ok := algorithm.CipherById[pk.kdf.Bytes()[2]] |
||||
if !ok { |
||||
return errors.UnsupportedError("unsupported ECDH KDF cipher: " + strconv.Itoa(int(pk.kdf.Bytes()[2]))) |
||||
} |
||||
|
||||
pk.PublicKey = &ecdh.PublicKey{ |
||||
CurveType: cType, |
||||
Curve: c, |
||||
X: x, |
||||
Y: y, |
||||
KDF: ecdh.KDF{ |
||||
Hash: kdfHash, |
||||
Cipher: kdfCipher, |
||||
}, |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) { |
||||
pk.oid = new(encoding.OID) |
||||
if _, err = pk.oid.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
curveInfo := ecc.FindByOid(pk.oid) |
||||
if curveInfo == nil || curveInfo.SigAlgorithm != ecc.EdDSA { |
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) |
||||
} |
||||
pk.p = new(encoding.MPI) |
||||
if _, err = pk.p.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
eddsa := make(ed25519.PublicKey, ed25519.PublicKeySize) |
||||
switch flag := pk.p.Bytes()[0]; flag { |
||||
case 0x04: |
||||
// TODO: see _grcy_ecc_eddsa_ensure_compact in grcypt
|
||||
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag))) |
||||
case 0x40: |
||||
copy(eddsa[:], pk.p.Bytes()[1:]) |
||||
default: |
||||
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag))) |
||||
} |
||||
|
||||
pk.PublicKey = &eddsa |
||||
return |
||||
} |
||||
|
||||
// SerializeForHash serializes the PublicKey to w with the special packet
|
||||
// header format needed for hashing.
|
||||
func (pk *PublicKey) SerializeForHash(w io.Writer) error { |
||||
pk.SerializeSignaturePrefix(w) |
||||
return pk.serializeWithoutHeaders(w) |
||||
} |
||||
|
||||
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
|
||||
// The prefix is used when calculating a signature over this public key. See
|
||||
// RFC 4880, section 5.2.4.
|
||||
func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) { |
||||
var pLength = pk.algorithmSpecificByteCount() |
||||
if pk.Version == 5 { |
||||
pLength += 10 // version, timestamp (4), algorithm, key octet count (4).
|
||||
w.Write([]byte{ |
||||
0x9A, |
||||
byte(pLength >> 24), |
||||
byte(pLength >> 16), |
||||
byte(pLength >> 8), |
||||
byte(pLength), |
||||
}) |
||||
return |
||||
} |
||||
pLength += 6 |
||||
w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) |
||||
} |
||||
|
||||
func (pk *PublicKey) Serialize(w io.Writer) (err error) { |
||||
length := 6 // 6 byte header
|
||||
length += pk.algorithmSpecificByteCount() |
||||
if pk.Version == 5 { |
||||
length += 4 // octet key count
|
||||
} |
||||
packetType := packetTypePublicKey |
||||
if pk.IsSubkey { |
||||
packetType = packetTypePublicSubkey |
||||
} |
||||
err = serializeHeader(w, packetType, length) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return pk.serializeWithoutHeaders(w) |
||||
} |
||||
|
||||
func (pk *PublicKey) algorithmSpecificByteCount() int { |
||||
length := 0 |
||||
switch pk.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: |
||||
length += int(pk.n.EncodedLength()) |
||||
length += int(pk.e.EncodedLength()) |
||||
case PubKeyAlgoDSA: |
||||
length += int(pk.p.EncodedLength()) |
||||
length += int(pk.q.EncodedLength()) |
||||
length += int(pk.g.EncodedLength()) |
||||
length += int(pk.y.EncodedLength()) |
||||
case PubKeyAlgoElGamal: |
||||
length += int(pk.p.EncodedLength()) |
||||
length += int(pk.g.EncodedLength()) |
||||
length += int(pk.y.EncodedLength()) |
||||
case PubKeyAlgoECDSA: |
||||
length += int(pk.oid.EncodedLength()) |
||||
length += int(pk.p.EncodedLength()) |
||||
case PubKeyAlgoECDH: |
||||
length += int(pk.oid.EncodedLength()) |
||||
length += int(pk.p.EncodedLength()) |
||||
length += int(pk.kdf.EncodedLength()) |
||||
case PubKeyAlgoEdDSA: |
||||
length += int(pk.oid.EncodedLength()) |
||||
length += int(pk.p.EncodedLength()) |
||||
default: |
||||
panic("unknown public key algorithm") |
||||
} |
||||
return length |
||||
} |
||||
|
||||
// serializeWithoutHeaders marshals the PublicKey to w in the form of an
|
||||
// OpenPGP public key packet, not including the packet header.
|
||||
func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { |
||||
t := uint32(pk.CreationTime.Unix()) |
||||
if _, err = w.Write([]byte{ |
||||
byte(pk.Version), |
||||
byte(t >> 24), byte(t >> 16), byte(t >> 8), byte(t), |
||||
byte(pk.PubKeyAlgo), |
||||
}); err != nil { |
||||
return |
||||
} |
||||
|
||||
if pk.Version == 5 { |
||||
n := pk.algorithmSpecificByteCount() |
||||
if _, err = w.Write([]byte{ |
||||
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n), |
||||
}); err != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
switch pk.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: |
||||
if _, err = w.Write(pk.n.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(pk.e.EncodedBytes()) |
||||
return |
||||
case PubKeyAlgoDSA: |
||||
if _, err = w.Write(pk.p.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
if _, err = w.Write(pk.q.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
if _, err = w.Write(pk.g.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(pk.y.EncodedBytes()) |
||||
return |
||||
case PubKeyAlgoElGamal: |
||||
if _, err = w.Write(pk.p.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
if _, err = w.Write(pk.g.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(pk.y.EncodedBytes()) |
||||
return |
||||
case PubKeyAlgoECDSA: |
||||
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(pk.p.EncodedBytes()) |
||||
return |
||||
case PubKeyAlgoECDH: |
||||
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
if _, err = w.Write(pk.p.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(pk.kdf.EncodedBytes()) |
||||
return |
||||
case PubKeyAlgoEdDSA: |
||||
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(pk.p.EncodedBytes()) |
||||
return |
||||
} |
||||
return errors.InvalidArgumentError("bad public-key algorithm") |
||||
} |
||||
|
||||
// CanSign returns true iff this public key can generate signatures
|
||||
func (pk *PublicKey) CanSign() bool { |
||||
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH |
||||
} |
||||
|
||||
// VerifySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of the data hashed into signed. signed is mutated by this call.
|
||||
func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) { |
||||
if !pk.CanSign() { |
||||
return errors.InvalidArgumentError("public key cannot generate signatures") |
||||
} |
||||
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) { |
||||
sig.AddMetadataToHashSuffix() |
||||
} |
||||
signed.Write(sig.HashSuffix) |
||||
hashBytes := signed.Sum(nil) |
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { |
||||
return errors.SignatureError("hash tag doesn't match") |
||||
} |
||||
|
||||
if pk.PubKeyAlgo != sig.PubKeyAlgo { |
||||
return errors.InvalidArgumentError("public key and signature use different algorithms") |
||||
} |
||||
|
||||
switch pk.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: |
||||
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) |
||||
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.Bytes())) |
||||
if err != nil { |
||||
return errors.SignatureError("RSA verification failure") |
||||
} |
||||
return nil |
||||
case PubKeyAlgoDSA: |
||||
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) |
||||
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
||||
subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 |
||||
if len(hashBytes) > subgroupSize { |
||||
hashBytes = hashBytes[:subgroupSize] |
||||
} |
||||
if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) { |
||||
return errors.SignatureError("DSA verification failure") |
||||
} |
||||
return nil |
||||
case PubKeyAlgoECDSA: |
||||
ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey) |
||||
if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.Bytes()), new(big.Int).SetBytes(sig.ECDSASigS.Bytes())) { |
||||
return errors.SignatureError("ECDSA verification failure") |
||||
} |
||||
return nil |
||||
case PubKeyAlgoEdDSA: |
||||
eddsaPublicKey := pk.PublicKey.(*ed25519.PublicKey) |
||||
|
||||
sigR := sig.EdDSASigR.Bytes() |
||||
sigS := sig.EdDSASigS.Bytes() |
||||
|
||||
eddsaSig := make([]byte, ed25519.SignatureSize) |
||||
copy(eddsaSig[32-len(sigR):32], sigR) |
||||
copy(eddsaSig[64-len(sigS):], sigS) |
||||
|
||||
if !ed25519.Verify(*eddsaPublicKey, hashBytes, eddsaSig) { |
||||
return errors.SignatureError("EdDSA verification failure") |
||||
} |
||||
return nil |
||||
default: |
||||
return errors.SignatureError("Unsupported public key algorithm used in signature") |
||||
} |
||||
} |
||||
|
||||
// keySignatureHash returns a Hash of the message that needs to be signed for
|
||||
// pk to assert a subkey relationship to signed.
|
||||
func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { |
||||
if !hashFunc.Available() { |
||||
return nil, errors.UnsupportedError("hash function") |
||||
} |
||||
h = hashFunc.New() |
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
err = pk.SerializeForHash(h) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
err = signed.SerializeForHash(h) |
||||
return |
||||
} |
||||
|
||||
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of signed.
|
||||
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error { |
||||
h, err := keySignatureHash(pk, signed, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if err = pk.VerifySignature(h, sig); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if sig.FlagSign { |
||||
// Signing subkeys must be cross-signed. See
|
||||
// https://www.gnupg.org/faq/subkey-cross-certify.html.
|
||||
if sig.EmbeddedSignature == nil { |
||||
return errors.StructuralError("signing subkey is missing cross-signature") |
||||
} |
||||
// Verify the cross-signature. This is calculated over the same
|
||||
// data as the main signature, so we cannot just recursively
|
||||
// call signed.VerifyKeySignature(...)
|
||||
if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil { |
||||
return errors.StructuralError("error while hashing for cross-signature: " + err.Error()) |
||||
} |
||||
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil { |
||||
return errors.StructuralError("error while verifying cross-signature: " + err.Error()) |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { |
||||
if !hashFunc.Available() { |
||||
return nil, errors.UnsupportedError("hash function") |
||||
} |
||||
h = hashFunc.New() |
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
err = pk.SerializeForHash(h) |
||||
|
||||
return |
||||
} |
||||
|
||||
// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key.
|
||||
func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) { |
||||
h, err := keyRevocationHash(pk, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return pk.VerifySignature(h, sig) |
||||
} |
||||
|
||||
// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature,
|
||||
// made by the passed in signingKey.
|
||||
func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signingKey *PublicKey) (err error) { |
||||
h, err := keyRevocationHash(pk, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return signingKey.VerifySignature(h, sig) |
||||
} |
||||
|
||||
// userIdSignatureHash returns a Hash of the message that needs to be signed
|
||||
// to assert that pk is a valid key for id.
|
||||
func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { |
||||
if !hashFunc.Available() { |
||||
return nil, errors.UnsupportedError("hash function") |
||||
} |
||||
h = hashFunc.New() |
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h) |
||||
pk.serializeWithoutHeaders(h) |
||||
|
||||
var buf [5]byte |
||||
buf[0] = 0xb4 |
||||
buf[1] = byte(len(id) >> 24) |
||||
buf[2] = byte(len(id) >> 16) |
||||
buf[3] = byte(len(id) >> 8) |
||||
buf[4] = byte(len(id)) |
||||
h.Write(buf[:]) |
||||
h.Write([]byte(id)) |
||||
|
||||
return |
||||
} |
||||
|
||||
// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, that id is the identity of pub.
|
||||
func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) { |
||||
h, err := userIdSignatureHash(id, pub, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return pk.VerifySignature(h, sig) |
||||
} |
||||
|
||||
// KeyIdString returns the public key's fingerprint in capital hex
|
||||
// (e.g. "6C7EE1B8621CC013").
|
||||
func (pk *PublicKey) KeyIdString() string { |
||||
return fmt.Sprintf("%X", pk.Fingerprint[12:20]) |
||||
} |
||||
|
||||
// KeyIdShortString returns the short form of public key's fingerprint
|
||||
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
|
||||
func (pk *PublicKey) KeyIdShortString() string { |
||||
return fmt.Sprintf("%X", pk.Fingerprint[16:20]) |
||||
} |
||||
|
||||
// BitLength returns the bit length for the given public key.
|
||||
func (pk *PublicKey) BitLength() (bitLength uint16, err error) { |
||||
switch pk.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: |
||||
bitLength = pk.n.BitLength() |
||||
case PubKeyAlgoDSA: |
||||
bitLength = pk.p.BitLength() |
||||
case PubKeyAlgoElGamal: |
||||
bitLength = pk.p.BitLength() |
||||
case PubKeyAlgoECDSA: |
||||
bitLength = pk.p.BitLength() |
||||
case PubKeyAlgoECDH: |
||||
bitLength = pk.p.BitLength() |
||||
case PubKeyAlgoEdDSA: |
||||
bitLength = pk.p.BitLength() |
||||
default: |
||||
err = errors.InvalidArgumentError("bad public-key algorithm") |
||||
} |
||||
return |
||||
} |
||||
|
||||
// KeyExpired returns whether sig is a self-signature of a key that has
|
||||
// expired or is created in the future.
|
||||
func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool { |
||||
if pk.CreationTime.After(currentTime) { |
||||
return true |
||||
} |
||||
if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 { |
||||
return false |
||||
} |
||||
expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) |
||||
return currentTime.After(expiry) |
||||
} |
@ -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` |
@ -0,0 +1,78 @@ |
||||
// 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" |
||||
) |
||||
|
||||
// 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 packet types are skipped.
|
||||
func (r *Reader) Next() (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 == nil { |
||||
return |
||||
} |
||||
if err == io.EOF { |
||||
r.readers = r.readers[:len(r.readers)-1] |
||||
continue |
||||
} |
||||
// TODO: Add strict mode that rejects unknown packets, instead of ignoring them.
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); !ok { |
||||
return nil, 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}, |
||||
} |
||||
} |
@ -0,0 +1,964 @@ |
||||
// 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/dsa" |
||||
"crypto/ecdsa" |
||||
"encoding/asn1" |
||||
"encoding/binary" |
||||
"hash" |
||||
"io" |
||||
"math/big" |
||||
"strconv" |
||||
"time" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding" |
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k" |
||||
) |
||||
|
||||
const ( |
||||
// See RFC 4880, section 5.2.3.21 for details.
|
||||
KeyFlagCertify = 1 << iota |
||||
KeyFlagSign |
||||
KeyFlagEncryptCommunications |
||||
KeyFlagEncryptStorage |
||||
) |
||||
|
||||
// Signature represents a signature. See RFC 4880, section 5.2.
|
||||
type Signature struct { |
||||
Version int |
||||
SigType SignatureType |
||||
PubKeyAlgo PublicKeyAlgorithm |
||||
Hash crypto.Hash |
||||
|
||||
// HashSuffix is extra data that is hashed in after the signed data.
|
||||
HashSuffix []byte |
||||
// HashTag contains the first two bytes of the hash for fast rejection
|
||||
// of bad signed data.
|
||||
HashTag [2]byte |
||||
|
||||
// Metadata includes format, filename and time, and is protected by v5
|
||||
// signatures of type 0x00 or 0x01. This metadata is included into the hash
|
||||
// computation; if nil, six 0x00 bytes are used instead. See section 5.2.4.
|
||||
Metadata *LiteralData |
||||
|
||||
CreationTime time.Time |
||||
|
||||
RSASignature encoding.Field |
||||
DSASigR, DSASigS encoding.Field |
||||
ECDSASigR, ECDSASigS encoding.Field |
||||
EdDSASigR, EdDSASigS encoding.Field |
||||
|
||||
// rawSubpackets contains the unparsed subpackets, in order.
|
||||
rawSubpackets []outputSubpacket |
||||
|
||||
// The following are optional so are nil when not included in the
|
||||
// signature.
|
||||
|
||||
SigLifetimeSecs, KeyLifetimeSecs *uint32 |
||||
PreferredSymmetric, PreferredHash, PreferredCompression []uint8 |
||||
PreferredAEAD []uint8 |
||||
IssuerKeyId *uint64 |
||||
IssuerFingerprint []byte |
||||
IsPrimaryId *bool |
||||
|
||||
// FlagsValid is set if any flags were given. See RFC 4880, section
|
||||
// 5.2.3.21 for details.
|
||||
FlagsValid bool |
||||
FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool |
||||
|
||||
// RevocationReason is set if this signature has been revoked.
|
||||
// See RFC 4880, section 5.2.3.23 for details.
|
||||
RevocationReason *uint8 |
||||
RevocationReasonText string |
||||
|
||||
// In a self-signature, these flags are set there is a features subpacket
|
||||
// indicating that the issuer implementation supports these features
|
||||
// (section 5.2.5.25).
|
||||
MDC, AEAD, V5Keys bool |
||||
|
||||
// EmbeddedSignature, if non-nil, is a signature of the parent key, by
|
||||
// this key. This prevents an attacker from claiming another's signing
|
||||
// subkey as their own.
|
||||
EmbeddedSignature *Signature |
||||
|
||||
outSubpackets []outputSubpacket |
||||
} |
||||
|
||||
func (sig *Signature) parse(r io.Reader) (err error) { |
||||
// RFC 4880, section 5.2.3
|
||||
var buf [5]byte |
||||
_, err = readFull(r, buf[:1]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if buf[0] != 4 && buf[0] != 5 { |
||||
err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) |
||||
return |
||||
} |
||||
sig.Version = int(buf[0]) |
||||
_, err = readFull(r, buf[:5]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
sig.SigType = SignatureType(buf[0]) |
||||
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) |
||||
switch sig.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA: |
||||
default: |
||||
err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) |
||||
return |
||||
} |
||||
|
||||
var ok bool |
||||
sig.Hash, ok = s2k.HashIdToHash(buf[2]) |
||||
if !ok { |
||||
return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) |
||||
} |
||||
|
||||
hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) |
||||
hashedSubpackets := make([]byte, hashedSubpacketsLength) |
||||
_, err = readFull(r, hashedSubpackets) |
||||
if err != nil { |
||||
return |
||||
} |
||||
sig.buildHashSuffix(hashedSubpackets) |
||||
err = parseSignatureSubpackets(sig, hashedSubpackets, true) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
_, err = readFull(r, buf[:2]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) |
||||
unhashedSubpackets := make([]byte, unhashedSubpacketsLength) |
||||
_, err = readFull(r, unhashedSubpackets) |
||||
if err != nil { |
||||
return |
||||
} |
||||
err = parseSignatureSubpackets(sig, unhashedSubpackets, false) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
_, err = readFull(r, sig.HashTag[:2]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
switch sig.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: |
||||
sig.RSASignature = new(encoding.MPI) |
||||
_, err = sig.RSASignature.ReadFrom(r) |
||||
case PubKeyAlgoDSA: |
||||
sig.DSASigR = new(encoding.MPI) |
||||
if _, err = sig.DSASigR.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
sig.DSASigS = new(encoding.MPI) |
||||
_, err = sig.DSASigS.ReadFrom(r) |
||||
case PubKeyAlgoECDSA: |
||||
sig.ECDSASigR = new(encoding.MPI) |
||||
if _, err = sig.ECDSASigR.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
sig.ECDSASigS = new(encoding.MPI) |
||||
_, err = sig.ECDSASigS.ReadFrom(r) |
||||
case PubKeyAlgoEdDSA: |
||||
sig.EdDSASigR = new(encoding.MPI) |
||||
if _, err = sig.EdDSASigR.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
|
||||
sig.EdDSASigS = new(encoding.MPI) |
||||
if _, err = sig.EdDSASigS.ReadFrom(r); err != nil { |
||||
return |
||||
} |
||||
default: |
||||
panic("unreachable") |
||||
} |
||||
return |
||||
} |
||||
|
||||
// parseSignatureSubpackets parses subpackets of the main signature packet. See
|
||||
// RFC 4880, section 5.2.3.1.
|
||||
func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { |
||||
for len(subpackets) > 0 { |
||||
subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
if sig.CreationTime.IsZero() { |
||||
err = errors.StructuralError("no creation time in signature") |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
type signatureSubpacketType uint8 |
||||
|
||||
const ( |
||||
creationTimeSubpacket signatureSubpacketType = 2 |
||||
signatureExpirationSubpacket signatureSubpacketType = 3 |
||||
keyExpirationSubpacket signatureSubpacketType = 9 |
||||
prefSymmetricAlgosSubpacket signatureSubpacketType = 11 |
||||
issuerSubpacket signatureSubpacketType = 16 |
||||
prefHashAlgosSubpacket signatureSubpacketType = 21 |
||||
prefCompressionSubpacket signatureSubpacketType = 22 |
||||
primaryUserIdSubpacket signatureSubpacketType = 25 |
||||
keyFlagsSubpacket signatureSubpacketType = 27 |
||||
reasonForRevocationSubpacket signatureSubpacketType = 29 |
||||
featuresSubpacket signatureSubpacketType = 30 |
||||
embeddedSignatureSubpacket signatureSubpacketType = 32 |
||||
issuerFingerprintSubpacket signatureSubpacketType = 33 |
||||
prefAeadAlgosSubpacket signatureSubpacketType = 34 |
||||
) |
||||
|
||||
// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
|
||||
func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { |
||||
// RFC 4880, section 5.2.3.1
|
||||
var ( |
||||
length uint32 |
||||
packetType signatureSubpacketType |
||||
isCritical bool |
||||
) |
||||
switch { |
||||
case subpacket[0] < 192: |
||||
length = uint32(subpacket[0]) |
||||
subpacket = subpacket[1:] |
||||
case subpacket[0] < 255: |
||||
if len(subpacket) < 2 { |
||||
goto Truncated |
||||
} |
||||
length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 |
||||
subpacket = subpacket[2:] |
||||
default: |
||||
if len(subpacket) < 5 { |
||||
goto Truncated |
||||
} |
||||
length = uint32(subpacket[1])<<24 | |
||||
uint32(subpacket[2])<<16 | |
||||
uint32(subpacket[3])<<8 | |
||||
uint32(subpacket[4]) |
||||
subpacket = subpacket[5:] |
||||
} |
||||
if length > uint32(len(subpacket)) { |
||||
goto Truncated |
||||
} |
||||
rest = subpacket[length:] |
||||
subpacket = subpacket[:length] |
||||
if len(subpacket) == 0 { |
||||
err = errors.StructuralError("zero length signature subpacket") |
||||
return |
||||
} |
||||
packetType = signatureSubpacketType(subpacket[0] & 0x7f) |
||||
isCritical = subpacket[0]&0x80 == 0x80 |
||||
subpacket = subpacket[1:] |
||||
sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) |
||||
switch packetType { |
||||
case creationTimeSubpacket: |
||||
if !isHashed { |
||||
err = errors.StructuralError("signature creation time in non-hashed area") |
||||
return |
||||
} |
||||
if len(subpacket) != 4 { |
||||
err = errors.StructuralError("signature creation time not four bytes") |
||||
return |
||||
} |
||||
t := binary.BigEndian.Uint32(subpacket) |
||||
sig.CreationTime = time.Unix(int64(t), 0) |
||||
case signatureExpirationSubpacket: |
||||
// Signature expiration time, section 5.2.3.10
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
if len(subpacket) != 4 { |
||||
err = errors.StructuralError("expiration subpacket with bad length") |
||||
return |
||||
} |
||||
sig.SigLifetimeSecs = new(uint32) |
||||
*sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) |
||||
case keyExpirationSubpacket: |
||||
// Key expiration time, section 5.2.3.6
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
if len(subpacket) != 4 { |
||||
err = errors.StructuralError("key expiration subpacket with bad length") |
||||
return |
||||
} |
||||
sig.KeyLifetimeSecs = new(uint32) |
||||
*sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) |
||||
case prefSymmetricAlgosSubpacket: |
||||
// Preferred symmetric algorithms, section 5.2.3.7
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
sig.PreferredSymmetric = make([]byte, len(subpacket)) |
||||
copy(sig.PreferredSymmetric, subpacket) |
||||
case issuerSubpacket: |
||||
if sig.Version > 4 { |
||||
err = errors.StructuralError("issuer subpacket found in v5 key") |
||||
} |
||||
// Issuer, section 5.2.3.5
|
||||
if len(subpacket) != 8 { |
||||
err = errors.StructuralError("issuer subpacket with bad length") |
||||
return |
||||
} |
||||
sig.IssuerKeyId = new(uint64) |
||||
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) |
||||
case prefHashAlgosSubpacket: |
||||
// Preferred hash algorithms, section 5.2.3.8
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
sig.PreferredHash = make([]byte, len(subpacket)) |
||||
copy(sig.PreferredHash, subpacket) |
||||
case prefCompressionSubpacket: |
||||
// Preferred compression algorithms, section 5.2.3.9
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
sig.PreferredCompression = make([]byte, len(subpacket)) |
||||
copy(sig.PreferredCompression, subpacket) |
||||
case primaryUserIdSubpacket: |
||||
// Primary User ID, section 5.2.3.19
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
if len(subpacket) != 1 { |
||||
err = errors.StructuralError("primary user id subpacket with bad length") |
||||
return |
||||
} |
||||
sig.IsPrimaryId = new(bool) |
||||
if subpacket[0] > 0 { |
||||
*sig.IsPrimaryId = true |
||||
} |
||||
case keyFlagsSubpacket: |
||||
// Key flags, section 5.2.3.21
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
if len(subpacket) == 0 { |
||||
err = errors.StructuralError("empty key flags subpacket") |
||||
return |
||||
} |
||||
sig.FlagsValid = true |
||||
if subpacket[0]&KeyFlagCertify != 0 { |
||||
sig.FlagCertify = true |
||||
} |
||||
if subpacket[0]&KeyFlagSign != 0 { |
||||
sig.FlagSign = true |
||||
} |
||||
if subpacket[0]&KeyFlagEncryptCommunications != 0 { |
||||
sig.FlagEncryptCommunications = true |
||||
} |
||||
if subpacket[0]&KeyFlagEncryptStorage != 0 { |
||||
sig.FlagEncryptStorage = true |
||||
} |
||||
case reasonForRevocationSubpacket: |
||||
// Reason For Revocation, section 5.2.3.23
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
if len(subpacket) == 0 { |
||||
err = errors.StructuralError("empty revocation reason subpacket") |
||||
return |
||||
} |
||||
sig.RevocationReason = new(uint8) |
||||
*sig.RevocationReason = subpacket[0] |
||||
sig.RevocationReasonText = string(subpacket[1:]) |
||||
case featuresSubpacket: |
||||
// Features subpacket, section 5.2.3.24 specifies a very general
|
||||
// mechanism for OpenPGP implementations to signal support for new
|
||||
// features.
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
if len(subpacket) > 0 { |
||||
if subpacket[0]&0x01 != 0 { |
||||
sig.MDC = true |
||||
} |
||||
if subpacket[0]&0x02 != 0 { |
||||
sig.AEAD = true |
||||
} |
||||
if subpacket[0]&0x04 != 0 { |
||||
sig.V5Keys = true |
||||
} |
||||
} |
||||
case embeddedSignatureSubpacket: |
||||
// Only usage is in signatures that cross-certify
|
||||
// signing subkeys. section 5.2.3.26 describes the
|
||||
// format, with its usage described in section 11.1
|
||||
if sig.EmbeddedSignature != nil { |
||||
err = errors.StructuralError("Cannot have multiple embedded signatures") |
||||
return |
||||
} |
||||
sig.EmbeddedSignature = new(Signature) |
||||
// Embedded signatures are required to be v4 signatures see
|
||||
// section 12.1. However, we only parse v4 signatures in this
|
||||
// file anyway.
|
||||
if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { |
||||
return nil, err |
||||
} |
||||
if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { |
||||
return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) |
||||
} |
||||
case issuerFingerprintSubpacket: |
||||
v, l := subpacket[0], len(subpacket[1:]) |
||||
if v == 5 && l != 32 || v != 5 && l != 20 { |
||||
return nil, errors.StructuralError("bad fingerprint length") |
||||
} |
||||
sig.IssuerFingerprint = make([]byte, l) |
||||
copy(sig.IssuerFingerprint, subpacket[1:]) |
||||
sig.IssuerKeyId = new(uint64) |
||||
if v == 5 { |
||||
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[1:9]) |
||||
} else { |
||||
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21]) |
||||
} |
||||
case prefAeadAlgosSubpacket: |
||||
// Preferred symmetric algorithms, section 5.2.3.8
|
||||
if !isHashed { |
||||
return |
||||
} |
||||
sig.PreferredAEAD = make([]byte, len(subpacket)) |
||||
copy(sig.PreferredAEAD, subpacket) |
||||
default: |
||||
if isCritical { |
||||
err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) |
||||
return |
||||
} |
||||
} |
||||
return |
||||
|
||||
Truncated: |
||||
err = errors.StructuralError("signature subpacket truncated") |
||||
return |
||||
} |
||||
|
||||
// subpacketLengthLength returns the length, in bytes, of an encoded length value.
|
||||
func subpacketLengthLength(length int) int { |
||||
if length < 192 { |
||||
return 1 |
||||
} |
||||
if length < 16320 { |
||||
return 2 |
||||
} |
||||
return 5 |
||||
} |
||||
|
||||
func (sig *Signature) CheckKeyIdOrFingerprint(pk *PublicKey) bool { |
||||
if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) >= 20 { |
||||
return bytes.Equal(sig.IssuerFingerprint, pk.Fingerprint) |
||||
} |
||||
return sig.IssuerKeyId != nil && *sig.IssuerKeyId == pk.KeyId |
||||
} |
||||
|
||||
// serializeSubpacketLength marshals the given length into to.
|
||||
func serializeSubpacketLength(to []byte, length int) int { |
||||
// RFC 4880, Section 4.2.2.
|
||||
if length < 192 { |
||||
to[0] = byte(length) |
||||
return 1 |
||||
} |
||||
if length < 16320 { |
||||
length -= 192 |
||||
to[0] = byte((length >> 8) + 192) |
||||
to[1] = byte(length) |
||||
return 2 |
||||
} |
||||
to[0] = 255 |
||||
to[1] = byte(length >> 24) |
||||
to[2] = byte(length >> 16) |
||||
to[3] = byte(length >> 8) |
||||
to[4] = byte(length) |
||||
return 5 |
||||
} |
||||
|
||||
// subpacketsLength returns the serialized length, in bytes, of the given
|
||||
// subpackets.
|
||||
func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { |
||||
for _, subpacket := range subpackets { |
||||
if subpacket.hashed == hashed { |
||||
length += subpacketLengthLength(len(subpacket.contents) + 1) |
||||
length += 1 // type byte
|
||||
length += len(subpacket.contents) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// serializeSubpackets marshals the given subpackets into to.
|
||||
func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { |
||||
for _, subpacket := range subpackets { |
||||
if subpacket.hashed == hashed { |
||||
n := serializeSubpacketLength(to, len(subpacket.contents)+1) |
||||
to[n] = byte(subpacket.subpacketType) |
||||
to = to[1+n:] |
||||
n = copy(to, subpacket.contents) |
||||
to = to[n:] |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// SigExpired returns whether sig is a signature that has expired or is created
|
||||
// in the future.
|
||||
func (sig *Signature) SigExpired(currentTime time.Time) bool { |
||||
if sig.CreationTime.After(currentTime) { |
||||
return true |
||||
} |
||||
if sig.SigLifetimeSecs == nil || *sig.SigLifetimeSecs == 0 { |
||||
return false |
||||
} |
||||
expiry := sig.CreationTime.Add(time.Duration(*sig.SigLifetimeSecs) * time.Second) |
||||
return currentTime.After(expiry) |
||||
} |
||||
|
||||
// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
|
||||
func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) { |
||||
hash, ok := s2k.HashToHashId(sig.Hash) |
||||
if !ok { |
||||
sig.HashSuffix = nil |
||||
return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) |
||||
} |
||||
|
||||
hashedFields := bytes.NewBuffer([]byte{ |
||||
uint8(sig.Version), |
||||
uint8(sig.SigType), |
||||
uint8(sig.PubKeyAlgo), |
||||
uint8(hash), |
||||
uint8(len(hashedSubpackets) >> 8), |
||||
uint8(len(hashedSubpackets)), |
||||
}) |
||||
hashedFields.Write(hashedSubpackets) |
||||
|
||||
var l uint64 = uint64(6 + len(hashedSubpackets)) |
||||
if sig.Version == 5 { |
||||
hashedFields.Write([]byte{0x05, 0xff}) |
||||
hashedFields.Write([]byte{ |
||||
uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32), |
||||
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), |
||||
}) |
||||
} else { |
||||
hashedFields.Write([]byte{0x04, 0xff}) |
||||
hashedFields.Write([]byte{ |
||||
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), |
||||
}) |
||||
} |
||||
sig.HashSuffix = make([]byte, hashedFields.Len()) |
||||
copy(sig.HashSuffix, hashedFields.Bytes()) |
||||
return |
||||
} |
||||
|
||||
func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { |
||||
hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) |
||||
hashedSubpackets := make([]byte, hashedSubpacketsLen) |
||||
serializeSubpackets(hashedSubpackets, sig.outSubpackets, true) |
||||
err = sig.buildHashSuffix(hashedSubpackets) |
||||
if err != nil { |
||||
return |
||||
} |
||||
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) { |
||||
sig.AddMetadataToHashSuffix() |
||||
} |
||||
|
||||
h.Write(sig.HashSuffix) |
||||
digest = h.Sum(nil) |
||||
copy(sig.HashTag[:], digest) |
||||
return |
||||
} |
||||
|
||||
// Sign signs a message with a private key. The hash, h, must contain
|
||||
// the hash of the message to be signed and will be mutated by this function.
|
||||
// On success, the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { |
||||
if priv.Dummy() { |
||||
return errors.ErrDummyPrivateKey("dummy key found") |
||||
} |
||||
sig.Version = priv.PublicKey.Version |
||||
sig.IssuerFingerprint = priv.PublicKey.Fingerprint |
||||
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
digest, err := sig.signPrepareHash(h) |
||||
if err != nil { |
||||
return |
||||
} |
||||
switch priv.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: |
||||
// supports both *rsa.PrivateKey and crypto.Signer
|
||||
sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) |
||||
if err == nil { |
||||
sig.RSASignature = encoding.NewMPI(sigdata) |
||||
} |
||||
case PubKeyAlgoDSA: |
||||
dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) |
||||
|
||||
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
||||
subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 |
||||
if len(digest) > subgroupSize { |
||||
digest = digest[:subgroupSize] |
||||
} |
||||
r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) |
||||
if err == nil { |
||||
sig.DSASigR = new(encoding.MPI).SetBig(r) |
||||
sig.DSASigS = new(encoding.MPI).SetBig(s) |
||||
} |
||||
case PubKeyAlgoECDSA: |
||||
var r, s *big.Int |
||||
if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok { |
||||
// direct support, avoid asn1 wrapping/unwrapping
|
||||
r, s, err = ecdsa.Sign(config.Random(), pk, digest) |
||||
} else { |
||||
var b []byte |
||||
b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) |
||||
if err == nil { |
||||
r, s, err = unwrapECDSASig(b) |
||||
} |
||||
} |
||||
if err == nil { |
||||
sig.ECDSASigR = new(encoding.MPI).SetBig(r) |
||||
sig.ECDSASigS = new(encoding.MPI).SetBig(s) |
||||
} |
||||
case PubKeyAlgoEdDSA: |
||||
sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, crypto.Hash(0)) |
||||
if err == nil { |
||||
sig.EdDSASigR = encoding.NewMPI(sigdata[:32]) |
||||
sig.EdDSASigS = encoding.NewMPI(sigdata[32:]) |
||||
} |
||||
default: |
||||
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA
|
||||
// signature.
|
||||
func unwrapECDSASig(b []byte) (r, s *big.Int, err error) { |
||||
var ecsdaSig struct { |
||||
R, S *big.Int |
||||
} |
||||
_, err = asn1.Unmarshal(b, &ecsdaSig) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return ecsdaSig.R, ecsdaSig.S, nil |
||||
} |
||||
|
||||
// SignUserId computes a signature from priv, asserting that pub is a valid
|
||||
// key for the identity id. On success, the signature is stored in sig. Call
|
||||
// Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { |
||||
if priv.Dummy() { |
||||
return errors.ErrDummyPrivateKey("dummy key found") |
||||
} |
||||
h, err := userIdSignatureHash(id, pub, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return sig.Sign(h, priv, config) |
||||
} |
||||
|
||||
// CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success,
|
||||
// the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) CrossSignKey(pub *PublicKey, hashKey *PublicKey, signingKey *PrivateKey, |
||||
config *Config) error { |
||||
h, err := keySignatureHash(hashKey, pub, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return sig.Sign(h, signingKey, config) |
||||
} |
||||
|
||||
// SignKey computes a signature from priv, asserting that pub is a subkey. On
|
||||
// success, the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { |
||||
if priv.Dummy() { |
||||
return errors.ErrDummyPrivateKey("dummy key found") |
||||
} |
||||
h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return sig.Sign(h, priv, config) |
||||
} |
||||
|
||||
// RevokeKey computes a revocation signature of pub using priv. On success, the signature is
|
||||
// stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) RevokeKey(pub *PublicKey, priv *PrivateKey, config *Config) error { |
||||
h, err := keyRevocationHash(pub, sig.Hash) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return sig.Sign(h, priv, config) |
||||
} |
||||
|
||||
// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
|
||||
// called first.
|
||||
func (sig *Signature) Serialize(w io.Writer) (err error) { |
||||
if len(sig.outSubpackets) == 0 { |
||||
sig.outSubpackets = sig.rawSubpackets |
||||
} |
||||
if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil { |
||||
return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") |
||||
} |
||||
|
||||
sigLength := 0 |
||||
switch sig.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: |
||||
sigLength = int(sig.RSASignature.EncodedLength()) |
||||
case PubKeyAlgoDSA: |
||||
sigLength = int(sig.DSASigR.EncodedLength()) |
||||
sigLength += int(sig.DSASigS.EncodedLength()) |
||||
case PubKeyAlgoECDSA: |
||||
sigLength = int(sig.ECDSASigR.EncodedLength()) |
||||
sigLength += int(sig.ECDSASigS.EncodedLength()) |
||||
case PubKeyAlgoEdDSA: |
||||
sigLength = int(sig.EdDSASigR.EncodedLength()) |
||||
sigLength += int(sig.EdDSASigS.EncodedLength()) |
||||
default: |
||||
panic("impossible") |
||||
} |
||||
|
||||
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) |
||||
length := len(sig.HashSuffix) - 6 /* trailer not included */ + |
||||
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + |
||||
2 /* hash tag */ + sigLength |
||||
if sig.Version == 5 { |
||||
length -= 4 // eight-octet instead of four-octet big endian
|
||||
} |
||||
err = serializeHeader(w, packetTypeSignature, length) |
||||
if err != nil { |
||||
return |
||||
} |
||||
err = sig.serializeBody(w) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (sig *Signature) serializeBody(w io.Writer) (err error) { |
||||
hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) | uint16(sig.HashSuffix[5]) |
||||
fields := sig.HashSuffix[:6+hashedSubpacketsLen] |
||||
_, err = w.Write(fields) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) |
||||
unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) |
||||
unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) |
||||
unhashedSubpackets[1] = byte(unhashedSubpacketsLen) |
||||
serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) |
||||
|
||||
_, err = w.Write(unhashedSubpackets) |
||||
if err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(sig.HashTag[:]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
switch sig.PubKeyAlgo { |
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: |
||||
_, err = w.Write(sig.RSASignature.EncodedBytes()) |
||||
case PubKeyAlgoDSA: |
||||
if _, err = w.Write(sig.DSASigR.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(sig.DSASigS.EncodedBytes()) |
||||
case PubKeyAlgoECDSA: |
||||
if _, err = w.Write(sig.ECDSASigR.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(sig.ECDSASigS.EncodedBytes()) |
||||
case PubKeyAlgoEdDSA: |
||||
if _, err = w.Write(sig.EdDSASigR.EncodedBytes()); err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(sig.EdDSASigS.EncodedBytes()) |
||||
default: |
||||
panic("impossible") |
||||
} |
||||
return |
||||
} |
||||
|
||||
// outputSubpacket represents a subpacket to be marshaled.
|
||||
type outputSubpacket struct { |
||||
hashed bool // true if this subpacket is in the hashed area.
|
||||
subpacketType signatureSubpacketType |
||||
isCritical bool |
||||
contents []byte |
||||
} |
||||
|
||||
func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) { |
||||
creationTime := make([]byte, 4) |
||||
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) |
||||
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) |
||||
|
||||
if sig.IssuerKeyId != nil && sig.Version == 4 { |
||||
keyId := make([]byte, 8) |
||||
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) |
||||
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId}) |
||||
} |
||||
if sig.IssuerFingerprint != nil { |
||||
contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...) |
||||
subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, true, contents}) |
||||
} |
||||
if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { |
||||
sigLifetime := make([]byte, 4) |
||||
binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) |
||||
subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) |
||||
} |
||||
|
||||
// Key flags may only appear in self-signatures or certification signatures.
|
||||
|
||||
if sig.FlagsValid { |
||||
var flags byte |
||||
if sig.FlagCertify { |
||||
flags |= KeyFlagCertify |
||||
} |
||||
if sig.FlagSign { |
||||
flags |= KeyFlagSign |
||||
} |
||||
if sig.FlagEncryptCommunications { |
||||
flags |= KeyFlagEncryptCommunications |
||||
} |
||||
if sig.FlagEncryptStorage { |
||||
flags |= KeyFlagEncryptStorage |
||||
} |
||||
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) |
||||
} |
||||
|
||||
// The following subpackets may only appear in self-signatures.
|
||||
|
||||
var features = byte(0x00) |
||||
if sig.MDC { |
||||
features |= 0x01 |
||||
} |
||||
if sig.AEAD { |
||||
features |= 0x02 |
||||
} |
||||
if sig.V5Keys { |
||||
features |= 0x04 |
||||
} |
||||
|
||||
if features != 0x00 { |
||||
subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}}) |
||||
} |
||||
|
||||
if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { |
||||
keyLifetime := make([]byte, 4) |
||||
binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) |
||||
subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) |
||||
} |
||||
|
||||
if sig.IsPrimaryId != nil && *sig.IsPrimaryId { |
||||
subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) |
||||
} |
||||
|
||||
if len(sig.PreferredSymmetric) > 0 { |
||||
subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) |
||||
} |
||||
|
||||
if len(sig.PreferredHash) > 0 { |
||||
subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) |
||||
} |
||||
|
||||
if len(sig.PreferredCompression) > 0 { |
||||
subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) |
||||
} |
||||
|
||||
if len(sig.PreferredAEAD) > 0 { |
||||
subpackets = append(subpackets, outputSubpacket{true, prefAeadAlgosSubpacket, false, sig.PreferredAEAD}) |
||||
} |
||||
|
||||
// Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23.
|
||||
if sig.RevocationReason != nil { |
||||
subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true, |
||||
append([]uint8{*sig.RevocationReason}, []uint8(sig.RevocationReasonText)...)}) |
||||
} |
||||
|
||||
// EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26.
|
||||
if sig.EmbeddedSignature != nil { |
||||
var buf bytes.Buffer |
||||
err = sig.EmbeddedSignature.serializeBody(&buf) |
||||
if err != nil { |
||||
return |
||||
} |
||||
subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()}) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
// AddMetadataToHashSuffix modifies the current hash suffix to include metadata
|
||||
// (format, filename, and time). Version 5 keys protect this data including it
|
||||
// in the hash computation. See section 5.2.4.
|
||||
func (sig *Signature) AddMetadataToHashSuffix() { |
||||
if sig == nil || sig.Version != 5 { |
||||
return |
||||
} |
||||
if sig.SigType != 0x00 && sig.SigType != 0x01 { |
||||
return |
||||
} |
||||
lit := sig.Metadata |
||||
if lit == nil { |
||||
// This will translate into six 0x00 bytes.
|
||||
lit = &LiteralData{} |
||||
} |
||||
|
||||
// Extract the current byte count
|
||||
n := sig.HashSuffix[len(sig.HashSuffix)-8:] |
||||
l := uint64( |
||||
uint64(n[0])<<56 | uint64(n[1])<<48 | uint64(n[2])<<40 | uint64(n[3])<<32 | |
||||
uint64(n[4])<<24 | uint64(n[5])<<16 | uint64(n[6])<<8 | uint64(n[7])) |
||||
|
||||
suffix := bytes.NewBuffer(nil) |
||||
suffix.Write(sig.HashSuffix[:l]) |
||||
|
||||
// Add the metadata
|
||||
var buf [4]byte |
||||
buf[0] = lit.Format |
||||
fileName := lit.FileName |
||||
if len(lit.FileName) > 255 { |
||||
fileName = fileName[:255] |
||||
} |
||||
buf[1] = byte(len(fileName)) |
||||
suffix.Write(buf[:2]) |
||||
suffix.Write([]byte(lit.FileName)) |
||||
binary.BigEndian.PutUint32(buf[:], lit.Time) |
||||
suffix.Write(buf[:]) |
||||
|
||||
// Update the counter and restore trailing bytes
|
||||
l = uint64(suffix.Len()) |
||||
suffix.Write([]byte{0x05, 0xff}) |
||||
suffix.Write([]byte{ |
||||
uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32), |
||||
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), |
||||
}) |
||||
sig.HashSuffix = suffix.Bytes() |
||||
} |
267
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
267
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
@ -0,0 +1,267 @@ |
||||
// 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" |
||||
"io" |
||||
"strconv" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k" |
||||
) |
||||
|
||||
// This is the largest session key that we'll support. Since no 512-bit cipher
|
||||
// has even been seriously used, this is comfortably large.
|
||||
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) |
||||
aeadNonce []byte |
||||
encryptedKey []byte |
||||
} |
||||
|
||||
func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { |
||||
// RFC 4880, section 5.3.
|
||||
var buf [2]byte |
||||
if _, err := readFull(r, buf[:]); err != nil { |
||||
return err |
||||
} |
||||
ske.Version = int(buf[0]) |
||||
if ske.Version != 4 && ske.Version != 5 { |
||||
return errors.UnsupportedError("unknown SymmetricKeyEncrypted version") |
||||
} |
||||
ske.CipherFunc = CipherFunction(buf[1]) |
||||
if ske.CipherFunc.KeySize() == 0 { |
||||
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) |
||||
} |
||||
|
||||
if ske.Version == 5 { |
||||
mode := make([]byte, 1) |
||||
if _, err := r.Read(mode); err != nil { |
||||
return errors.StructuralError("cannot read AEAD octect from packet") |
||||
} |
||||
ske.Mode = AEADMode(mode[0]) |
||||
} |
||||
|
||||
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 nonce
|
||||
nonce := make([]byte, ske.Mode.NonceLength()) |
||||
_, err := readFull(r, nonce) |
||||
if err != nil && err != io.ErrUnexpectedEOF { |
||||
return err |
||||
} |
||||
ske.aeadNonce = nonce |
||||
} |
||||
|
||||
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: |
||||
plaintextKey, err := ske.decryptV5(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) decryptV5(key []byte) ([]byte, error) { |
||||
blockCipher := CipherFunction(ske.CipherFunc).new(key) |
||||
aead := ske.Mode.new(blockCipher) |
||||
|
||||
adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)} |
||||
plaintextKey, err := aead.Open(nil, ske.aeadNonce, 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 or SerializeAEADEncrypted, depending on
|
||||
// whether config.AEADConfig != nil.
|
||||
// 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() |
||||
keySize := cipherFunc.KeySize() |
||||
if keySize == 0 { |
||||
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) |
||||
} |
||||
|
||||
sessionKey := make([]byte, 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 session key must be passed to
|
||||
// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on
|
||||
// whether config.AEADConfig != nil.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) { |
||||
var version int |
||||
if config.AEAD() != nil { |
||||
version = 5 |
||||
} else { |
||||
version = 4 |
||||
} |
||||
cipherFunc := config.Cipher() |
||||
keySize := cipherFunc.KeySize() |
||||
if keySize == 0 { |
||||
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) |
||||
} |
||||
|
||||
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, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()}) |
||||
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: |
||||
nonceLen := config.AEAD().Mode().NonceLength() |
||||
tagLen := config.AEAD().Mode().TagLength() |
||||
packetLength = 3 + len(s2kBytes) + nonceLen + keySize + tagLen |
||||
} |
||||
err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
buf := make([]byte, 2) |
||||
// Symmetric Key Encrypted Version
|
||||
buf[0] = byte(version) |
||||
// Cipher function
|
||||
buf[1] = byte(cipherFunc) |
||||
|
||||
if version == 5 { |
||||
// AEAD mode
|
||||
buf = append(buf, byte(config.AEAD().Mode())) |
||||
} |
||||
_, 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: |
||||
blockCipher := cipherFunc.new(keyEncryptingKey) |
||||
mode := config.AEAD().Mode() |
||||
aead := mode.new(blockCipher) |
||||
// Sample nonce using random reader
|
||||
nonce := make([]byte, config.AEAD().Mode().NonceLength()) |
||||
_, err = io.ReadFull(config.Random(), nonce) |
||||
if err != nil { |
||||
return |
||||
} |
||||
// Seal and write (encryptedData includes auth. tag)
|
||||
adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)} |
||||
encryptedData := aead.Seal(nil, nonce, sessionKey, adata) |
||||
_, err = w.Write(nonce) |
||||
if err != nil { |
||||
return |
||||
} |
||||
_, err = w.Write(encryptedData) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
290
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
290
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
@ -0,0 +1,290 @@ |
||||
// 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" |
||||
) |
||||
|
||||
// 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 { |
||||
MDC bool // true iff this is a type 18 packet and thus has an embedded MAC.
|
||||
Contents io.Reader |
||||
prefix []byte |
||||
} |
||||
|
||||
const symmetricallyEncryptedVersion = 1 |
||||
|
||||
func (se *SymmetricallyEncrypted) parse(r io.Reader) error { |
||||
if se.MDC { |
||||
// See RFC 4880, section 5.13.
|
||||
var buf [1]byte |
||||
_, err := readFull(r, buf[:]) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if buf[0] != symmetricallyEncryptedVersion { |
||||
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) { |
||||
keySize := c.KeySize() |
||||
if keySize == 0 { |
||||
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) |
||||
} |
||||
if len(key) != 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.MDC { |
||||
// 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.MDC { |
||||
// MDC 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 seReader{plaintext}, nil |
||||
} |
||||
|
||||
// seReader wraps an io.Reader with a no-op Close method.
|
||||
type seReader struct { |
||||
in io.Reader |
||||
} |
||||
|
||||
func (ser seReader) Read(buf []byte) (int, error) { |
||||
return ser.in.Read(buf) |
||||
} |
||||
|
||||
func (ser seReader) Close() error { |
||||
return 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 (MDC) packet.
|
||||
const mdcPacketTagByte = byte(0x80) | 0x40 | 19 |
||||
|
||||
func (ser *seMDCReader) Close() error { |
||||
if ser.error { |
||||
return errors.ErrMDCMissing |
||||
} |
||||
|
||||
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.ErrMDCMissing |
||||
} |
||||
} |
||||
|
||||
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.ErrMDCMissing |
||||
} |
||||
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 |
||||
} |
||||
|
||||
// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
|
||||
// to w and returns a WriteCloser to which the to-be-encrypted packets can be
|
||||
// written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (Contents io.WriteCloser, err error) { |
||||
if c.KeySize() != len(key) { |
||||
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") |
||||
} |
||||
writeCloser := noOpCloser{w} |
||||
ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
_, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
block := c.new(key) |
||||
blockSize := block.BlockSize() |
||||
iv := make([]byte, blockSize) |
||||
_, err = config.Random().Read(iv) |
||||
if err != nil { |
||||
return |
||||
} |
||||
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 |
||||
} |
@ -0,0 +1,94 @@ |
||||
// 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" |
||||
"io/ioutil" |
||||
) |
||||
|
||||
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 |
||||
} |
||||
uat.Contents = append(uat.Contents, &OpaqueSubpacket{ |
||||
SubType: UserAttrImageSubpacket, |
||||
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 := ioutil.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 |
||||
} |
@ -0,0 +1,160 @@ |
||||
// 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" |
||||
"io/ioutil" |
||||
"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 := ioutil.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]) |
||||
return |
||||
} |
@ -0,0 +1,508 @@ |
||||
// 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" |
||||
"hash" |
||||
"io" |
||||
"strconv" |
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor" |
||||
"github.com/ProtonMail/go-crypto/openpgp/errors" |
||||
"github.com/ProtonMail/go-crypto/openpgp/packet" |
||||
) |
||||
|
||||
// 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.
|
||||
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: |
||||
break |
||||
default: |
||||
continue |
||||
} |
||||
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, *packet.AEADEncrypted: |
||||
edp = p.(packet.EncryptedDataPacket) |
||||
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) |
||||
// 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) |
||||
// TODO: ErrKeyIncorrect is no longer thrown on SEIP decryption,
|
||||
// but it might still be relevant for when we implement AEAD decryption (otherwise, remove?)
|
||||
if err != nil && err != errors.ErrKeyIncorrect { |
||||
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.StructuralError("parsing error") |
||||
} |
||||
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) |
||||
if err != nil { |
||||
md.SignatureError = err |
||||
} |
||||
|
||||
md.IsSigned = true |
||||
md.SignedByKeyId = p.KeyId |
||||
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} |
||||
} else { |
||||
md.UnverifiedBody = md.LiteralData.Body |
||||
} |
||||
|
||||
return md, nil |
||||
} |
||||
|
||||
// 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(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { |
||||
if hashId == crypto.MD5 { |
||||
return nil, nil, errors.UnsupportedError("insecure hash algorithm: MD5") |
||||
} |
||||
if !hashId.Available() { |
||||
return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) |
||||
} |
||||
h := hashId.New() |
||||
|
||||
switch sigType { |
||||
case packet.SigTypeBinary: |
||||
return h, h, nil |
||||
case packet.SigTypeText: |
||||
return h, NewCanonicalTextHash(h), 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 |
||||
} |
||||
|
||||
func (cr checkReader) Read(buf []byte) (int, error) { |
||||
n, sensitiveParsingError := cr.md.LiteralData.Body.Read(buf) |
||||
if sensitiveParsingError == io.EOF { |
||||
mdcErr := cr.md.decrypted.Close() |
||||
if mdcErr != nil { |
||||
return n, mdcErr |
||||
} |
||||
return n, io.EOF |
||||
} |
||||
|
||||
if sensitiveParsingError != nil { |
||||
return n, errors.StructuralError("parsing error") |
||||
} |
||||
|
||||
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]) |
||||
} |
||||
|
||||
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 { |
||||
scr.md.Signature = sig |
||||
scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) |
||||
if scr.md.SignatureError == nil && scr.md.Signature.SigExpired(scr.config.Now()) { |
||||
scr.md.SignatureError = errors.ErrSignatureExpired |
||||
} |
||||
} 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 { |
||||
mdcErr := scr.md.decrypted.Close() |
||||
if mdcErr != nil { |
||||
return n, mdcErr |
||||
} |
||||
} |
||||
return n, io.EOF |
||||
} |
||||
|
||||
if sensitiveParsingError != nil { |
||||
return n, errors.StructuralError("parsing error") |
||||
} |
||||
|
||||
return n, nil |
||||
} |
||||
|
||||
// CheckDetachedSignature takes a signed file and a detached signature and
|
||||
// returns the signer if the signature is valid. If the signer isn't known,
|
||||
// ErrUnknownIssuer is returned.
|
||||
func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) { |
||||
var expectedHashes []crypto.Hash |
||||
return CheckDetachedSignatureAndHash(keyring, signed, signature, expectedHashes, config) |
||||
} |
||||
|
||||
// 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) { |
||||
var issuerKeyId uint64 |
||||
var hashFunc crypto.Hash |
||||
var sigType packet.SignatureType |
||||
var keys []Key |
||||
var p packet.Packet |
||||
|
||||
expectedHashesLen := len(expectedHashes) |
||||
packets := packet.NewReader(signature) |
||||
var sig *packet.Signature |
||||
for { |
||||
p, err = packets.Next() |
||||
if err == io.EOF { |
||||
return nil, errors.ErrUnknownIssuer |
||||
} |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var ok bool |
||||
sig, ok = p.(*packet.Signature) |
||||
if !ok { |
||||
return nil, errors.StructuralError("non signature packet found") |
||||
} |
||||
if sig.IssuerKeyId == nil { |
||||
return nil, errors.StructuralError("signature doesn't have an issuer") |
||||
} |
||||
issuerKeyId = *sig.IssuerKeyId |
||||
hashFunc = sig.Hash |
||||
sigType = sig.SigType |
||||
|
||||
for i, expectedHash := range expectedHashes { |
||||
if hashFunc == expectedHash { |
||||
break |
||||
} |
||||
if i+1 == expectedHashesLen { |
||||
return nil, errors.StructuralError("hash algorithm mismatch with cleartext message headers") |
||||
} |
||||
} |
||||
|
||||
keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) |
||||
if len(keys) > 0 { |
||||
break |
||||
} |
||||
} |
||||
|
||||
if len(keys) == 0 { |
||||
panic("unreachable") |
||||
} |
||||
|
||||
h, wrappedHash, err := hashForSignature(hashFunc, sigType) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, key := range keys { |
||||
err = key.PublicKey.VerifySignature(h, sig) |
||||
if err == nil { |
||||
now := config.Now() |
||||
if sig.SigExpired(now) { |
||||
return key.Entity, errors.ErrSignatureExpired |
||||
} |
||||
if key.PublicKey.KeyExpired(key.SelfSignature, now) { |
||||
return key.Entity, errors.ErrKeyExpired |
||||
} |
||||
return key.Entity, nil |
||||
} |
||||
} |
||||
|
||||
return 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) |
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,367 @@ |
||||
// 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.
|
||||
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" |
||||
) |
||||
|
||||
// Config collects configuration parameters for s2k key-stretching
|
||||
// transformations. A nil *Config is valid and results in all default
|
||||
// values. Currently, Config is used only by the Serialize function in
|
||||
// this package.
|
||||
type Config struct { |
||||
// S2KMode is the mode of s2k function.
|
||||
// It can be 0 (simple), 1(salted), 3(iterated)
|
||||
// 2(reserved) 100-110(private/experimental).
|
||||
S2KMode uint8 |
||||
// Hash is the default hash function to be used. If
|
||||
// nil, SHA256 is used.
|
||||
Hash crypto.Hash |
||||
// S2KCount is only used for symmetric encryption. 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. See RFC 4880 Section 3.7.1.3.
|
||||
S2KCount int |
||||
} |
||||
|
||||
// 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 uint8 |
||||
// 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
|
||||
salt []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 |
||||
} |
||||
|
||||
func (c *Config) hash() crypto.Hash { |
||||
if c == nil || uint(c.Hash) == 0 { |
||||
return crypto.SHA256 |
||||
} |
||||
|
||||
return c.Hash |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
} |
||||
|
||||
// Generate generates valid parameters from given configuration.
|
||||
// It will enforce salted + hashed s2k method
|
||||
func Generate(rand io.Reader, c *Config) (*Params, error) { |
||||
hashId, ok := HashToHashId(c.Hash) |
||||
if !ok { |
||||
return nil, errors.UnsupportedError("no such hash") |
||||
} |
||||
|
||||
params := &Params{ |
||||
mode: 3, // Enforce iterared + salted method
|
||||
hashId: hashId, |
||||
salt: make([]byte, 8), |
||||
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 [9]byte |
||||
|
||||
_, err = io.ReadFull(r, buf[:2]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
params = &Params{ |
||||
mode: buf[0], |
||||
hashId: buf[1], |
||||
} |
||||
|
||||
switch params.mode { |
||||
case 0: |
||||
return params, nil |
||||
case 1: |
||||
_, err = io.ReadFull(r, buf[:8]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
params.salt = buf[:8] |
||||
return params, nil |
||||
case 3: |
||||
_, err = io.ReadFull(r, buf[:9]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
params.salt = buf[:8] |
||||
params.countByte = buf[8] |
||||
return params, nil |
||||
case 101: |
||||
// 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[:4]); err != nil { |
||||
return nil, err |
||||
} |
||||
if buf[0] == 'G' && buf[1] == 'N' && buf[2] == 'U' && buf[3] == 1 { |
||||
return params, nil |
||||
} |
||||
return nil, errors.UnsupportedError("GNU S2K extension") |
||||
} |
||||
|
||||
return nil, errors.UnsupportedError("S2K function") |
||||
} |
||||
|
||||
func (params *Params) Dummy() bool { |
||||
return params != nil && params.mode == 101 |
||||
} |
||||
|
||||
func (params *Params) Function() (f func(out, in []byte), err error) { |
||||
if params.Dummy() { |
||||
return nil, errors.ErrDummyPrivateKey("dummy key found") |
||||
} |
||||
hashObj, ok := HashIdToHash(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 0: |
||||
f := func(out, in []byte) { |
||||
Simple(out, hashObj.New(), in) |
||||
} |
||||
|
||||
return f, nil |
||||
case 1: |
||||
f := func(out, in []byte) { |
||||
Salted(out, hashObj.New(), in, params.salt) |
||||
} |
||||
|
||||
return f, nil |
||||
case 3: |
||||
f := func(out, in []byte) { |
||||
Iterated(out, hashObj.New(), in, params.salt, decodeCount(params.countByte)) |
||||
} |
||||
|
||||
return f, nil |
||||
} |
||||
|
||||
return nil, errors.UnsupportedError("S2K function") |
||||
} |
||||
|
||||
func (params *Params) Serialize(w io.Writer) (err error) { |
||||
if _, err = w.Write([]byte{params.mode}); err != nil { |
||||
return |
||||
} |
||||
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 == 3 { |
||||
_, err = w.Write([]byte{params.countByte}) |
||||
} |
||||
} |
||||
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 |
||||
} |
||||
|
||||
// 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 := algorithm.HashById[id]; ok { |
||||
return hash.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 := algorithm.HashById[id]; ok { |
||||
return hash.String(), true |
||||
} |
||||
return "", false |
||||
} |
||||
|
||||
// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash.
|
||||
func HashToHashId(h crypto.Hash) (id byte, ok bool) { |
||||
for id, hash := range algorithm.HashById { |
||||
if hash.HashFunc() == h { |
||||
return id, true |
||||
} |
||||
} |
||||
return 0, false |
||||
} |
@ -0,0 +1,568 @@ |
||||
// 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/packet" |
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k" |
||||
) |
||||
|
||||
// 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") |
||||
} |
||||
|
||||
sig := new(packet.Signature) |
||||
sig.SigType = sigType |
||||
sig.PubKeyAlgo = signingKey.PrivateKey.PubKeyAlgo |
||||
sig.Hash = config.Hash() |
||||
sig.CreationTime = config.Now() |
||||
sigLifetimeSecs := config.SigLifetime() |
||||
sig.SigLifetimeSecs = &sigLifetimeSecs |
||||
sig.IssuerKeyId = &signingKey.PrivateKey.KeyId |
||||
|
||||
h, wrappedHash, err := hashForSignature(sig.Hash, 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 |
||||
if config.AEAD() != nil { |
||||
w, err = packet.SerializeAEADEncrypted(ciphertext, key, config.Cipher(), config.AEAD().Mode(), config) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} else { |
||||
w, err = packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), 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] |
||||
} |
||||
|
||||
func hashToHashId(h crypto.Hash) uint8 { |
||||
v, ok := s2k.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 := s2k.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 := s2k.HashIdToHash(hashId); ok && h == configuredHash { |
||||
hash = h |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
if hash == 0 { |
||||
hashId := candidateHashes[0] |
||||
name, ok := s2k.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.)") |
||||
} |
||||
|
||||
if signer != nil { |
||||
ops := &packet.OnePassSignature{ |
||||
SigType: sigType, |
||||
Hash: hash, |
||||
PubKeyAlgo: signer.PubKeyAlgo, |
||||
KeyId: signer.KeyId, |
||||
IsLast: true, |
||||
} |
||||
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) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
metadata := &packet.LiteralData{ |
||||
Format: 't', |
||||
FileName: hints.FileName, |
||||
Time: epochSeconds, |
||||
} |
||||
if hints.IsBinary { |
||||
metadata.Format = 'b' |
||||
} |
||||
return signatureWriter{payload, literalData, hash, wrappedHash, h, 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.CipherAES128), |
||||
uint8(packet.CipherAES256), |
||||
uint8(packet.CipherCAST5), |
||||
} |
||||
// 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.SHA1), |
||||
hashToHashId(crypto.RIPEMD160), |
||||
} |
||||
candidateAeadModes := []uint8{ |
||||
uint8(packet.AEADModeEAX), |
||||
uint8(packet.AEADModeOCB), |
||||
uint8(packet.AEADModeExperimentalGCM), |
||||
} |
||||
candidateCompression := []uint8{ |
||||
uint8(packet.CompressionNone), |
||||
uint8(packet.CompressionZIP), |
||||
uint8(packet.CompressionZLIB), |
||||
} |
||||
// In the event that a recipient doesn't specify any supported ciphers
|
||||
// or hash functions, these are the ones that we assume that every
|
||||
// implementation supports.
|
||||
defaultCiphers := candidateCiphers[0:1] |
||||
defaultHashes := candidateHashes[0:1] |
||||
defaultAeadModes := candidateAeadModes[0:1] |
||||
defaultCompression := candidateCompression[0:1] |
||||
|
||||
encryptKeys := make([]Key, len(to)) |
||||
// AEAD is used only if every key supports it.
|
||||
aeadSupported := true |
||||
|
||||
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 encryption keys") |
||||
} |
||||
|
||||
sig := to[i].PrimaryIdentity().SelfSignature |
||||
if sig.AEAD == false { |
||||
aeadSupported = false |
||||
} |
||||
|
||||
preferredSymmetric := sig.PreferredSymmetric |
||||
if len(preferredSymmetric) == 0 { |
||||
preferredSymmetric = defaultCiphers |
||||
} |
||||
preferredHashes := sig.PreferredHash |
||||
if len(preferredHashes) == 0 { |
||||
preferredHashes = defaultHashes |
||||
} |
||||
preferredAeadModes := sig.PreferredAEAD |
||||
if len(preferredAeadModes) == 0 { |
||||
preferredAeadModes = defaultAeadModes |
||||
} |
||||
preferredCompression := sig.PreferredCompression |
||||
if len(preferredCompression) == 0 { |
||||
preferredCompression = defaultCompression |
||||
} |
||||
candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) |
||||
candidateHashes = intersectPreferences(candidateHashes, preferredHashes) |
||||
candidateAeadModes = intersectPreferences(candidateAeadModes, preferredAeadModes) |
||||
candidateCompression = intersectPreferences(candidateCompression, preferredCompression) |
||||
} |
||||
|
||||
if len(candidateCiphers) == 0 || len(candidateHashes) == 0 || len(candidateAeadModes) == 0 { |
||||
return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") |
||||
} |
||||
|
||||
cipher := packet.CipherFunction(candidateCiphers[0]) |
||||
mode := packet.AEADMode(candidateAeadModes[0]) |
||||
// 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 |
||||
} |
||||
} |
||||
|
||||
symKey := make([]byte, cipher.KeySize()) |
||||
if _, err := io.ReadFull(config.Random(), symKey); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, key := range encryptKeys { |
||||
if err := packet.SerializeEncryptedKey(keyWriter, key.PublicKey, cipher, symKey, config); err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
|
||||
var payload io.WriteCloser |
||||
if aeadSupported { |
||||
payload, err = packet.SerializeAEADEncrypted(dataWriter, symKey, cipher, mode, config) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} else { |
||||
payload, err = packet.SerializeSymmetricallyEncrypted(dataWriter, cipher, 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.SHA1), |
||||
hashToHashId(crypto.RIPEMD160), |
||||
} |
||||
defaultHashes := candidateHashes[0:1] |
||||
preferredHashes := signed.PrimaryIdentity().SelfSignature.PreferredHash |
||||
if len(preferredHashes) == 0 { |
||||
preferredHashes = defaultHashes |
||||
} |
||||
candidateHashes = intersectPreferences(candidateHashes, preferredHashes) |
||||
if len(candidateHashes) == 0 { |
||||
return nil, errors.InvalidArgumentError("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 |
||||
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 := &packet.Signature{ |
||||
Version: s.signer.Version, |
||||
SigType: s.sigType, |
||||
PubKeyAlgo: s.signer.PubKeyAlgo, |
||||
Hash: s.hashType, |
||||
CreationTime: s.config.Now(), |
||||
IssuerKeyId: &s.signer.KeyId, |
||||
Metadata: s.metadata, |
||||
} |
||||
|
||||
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() |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
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 |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,42 @@ |
||||
# bufpipe: Buffered Pipe |
||||
|
||||
[![CircleCI](https://img.shields.io/circleci/build/github/acomagu/bufpipe.svg?style=flat-square)](https://circleci.com/gh/acomagu/bufpipe) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/acomagu/bufpipe) |
||||
|
||||
The buffered version of io.Pipe. It's safe for concurrent use. |
||||
|
||||
## How does it differ from io.Pipe? |
||||
|
||||
Writes never block because the pipe has variable-sized buffer. |
||||
|
||||
```Go |
||||
r, w := bufpipe.New(nil) |
||||
io.WriteString(w, "abc") // No blocking. |
||||
io.WriteString(w, "def") // No blocking, too. |
||||
w.Close() |
||||
io.Copy(os.Stdout, r) |
||||
// Output: abcdef |
||||
``` |
||||
|
||||
[Playground](https://play.golang.org/p/PdyBAS3pVob) |
||||
|
||||
## How does it differ from bytes.Buffer? |
||||
|
||||
Reads block if the internal buffer is empty until the writer is closed. |
||||
|
||||
```Go |
||||
r, w := bufpipe.New(nil) |
||||
|
||||
done := make(chan struct{}) |
||||
go func() { |
||||
io.Copy(os.Stdout, r) // The reads block until the writer is closed. |
||||
done <- struct{}{} |
||||
}() |
||||
|
||||
io.WriteString(w, "abc") |
||||
io.WriteString(w, "def") |
||||
w.Close() |
||||
<-done |
||||
// Output: abcdef |
||||
``` |
||||
|
||||
[Playground](https://play.golang.org/p/UppmyLeRgX6) |
@ -0,0 +1,128 @@ |
||||
package bufpipe |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"io" |
||||
"sync" |
||||
) |
||||
|
||||
// ErrClosedPipe is the error used for read or write operations on a closed pipe.
|
||||
var ErrClosedPipe = errors.New("bufpipe: read/write on closed pipe") |
||||
|
||||
type pipe struct { |
||||
cond *sync.Cond |
||||
buf *bytes.Buffer |
||||
rerr, werr error |
||||
} |
||||
|
||||
// A PipeReader is the read half of a pipe.
|
||||
type PipeReader struct { |
||||
*pipe |
||||
} |
||||
|
||||
// A PipeWriter is the write half of a pipe.
|
||||
type PipeWriter struct { |
||||
*pipe |
||||
} |
||||
|
||||
// New creates a synchronous pipe using buf as its initial contents. It can be
|
||||
// used to connect code expecting an io.Reader with code expecting an io.Writer.
|
||||
//
|
||||
// Unlike io.Pipe, writes never block because the internal buffer has variable
|
||||
// size. Reads block only when the buffer is empty.
|
||||
//
|
||||
// It is safe to call Read and Write in parallel with each other or with Close.
|
||||
// Parallel calls to Read and parallel calls to Write are also safe: the
|
||||
// individual calls will be gated sequentially.
|
||||
//
|
||||
// The new pipe takes ownership of buf, and the caller should not use buf after
|
||||
// this call. New is intended to prepare a PipeReader to read existing data. It
|
||||
// can also be used to set the initial size of the internal buffer for writing.
|
||||
// To do that, buf should have the desired capacity but a length of zero.
|
||||
func New(buf []byte) (*PipeReader, *PipeWriter) { |
||||
p := &pipe{ |
||||
buf: bytes.NewBuffer(buf), |
||||
cond: sync.NewCond(new(sync.Mutex)), |
||||
} |
||||
return &PipeReader{ |
||||
pipe: p, |
||||
}, &PipeWriter{ |
||||
pipe: p, |
||||
} |
||||
} |
||||
|
||||
// Read implements the standard Read interface: it reads data from the pipe,
|
||||
// reading from the internal buffer, otherwise blocking until a writer arrives
|
||||
// or the write end is closed. If the write end is closed with an error, that
|
||||
// error is returned as err; otherwise err is io.EOF.
|
||||
func (r *PipeReader) Read(data []byte) (int, error) { |
||||
r.cond.L.Lock() |
||||
defer r.cond.L.Unlock() |
||||
|
||||
RETRY: |
||||
n, err := r.buf.Read(data) |
||||
// If not closed and no read, wait for writing.
|
||||
if err == io.EOF && r.rerr == nil && n == 0 { |
||||
r.cond.Wait() |
||||
goto RETRY |
||||
} |
||||
if err == io.EOF { |
||||
return n, r.rerr |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
// Close closes the reader; subsequent writes from the write half of the pipe
|
||||
// will return error ErrClosedPipe.
|
||||
func (r *PipeReader) Close() error { |
||||
return r.CloseWithError(nil) |
||||
} |
||||
|
||||
// CloseWithError closes the reader; subsequent writes to the write half of the
|
||||
// pipe will return the error err.
|
||||
func (r *PipeReader) CloseWithError(err error) error { |
||||
r.cond.L.Lock() |
||||
defer r.cond.L.Unlock() |
||||
|
||||
if err == nil { |
||||
err = ErrClosedPipe |
||||
} |
||||
r.werr = err |
||||
return nil |
||||
} |
||||
|
||||
// Write implements the standard Write interface: it writes data to the internal
|
||||
// buffer. If the read end is closed with an error, that err is returned as err;
|
||||
// otherwise err is ErrClosedPipe.
|
||||
func (w *PipeWriter) Write(data []byte) (int, error) { |
||||
w.cond.L.Lock() |
||||
defer w.cond.L.Unlock() |
||||
|
||||
if w.werr != nil { |
||||
return 0, w.werr |
||||
} |
||||
|
||||
n, err := w.buf.Write(data) |
||||
w.cond.Signal() |
||||
return n, err |
||||
} |
||||
|
||||
// Close closes the writer; subsequent reads from the read half of the pipe will
|
||||
// return io.EOF once the internal buffer get empty.
|
||||
func (w *PipeWriter) Close() error { |
||||
return w.CloseWithError(nil) |
||||
} |
||||
|
||||
// Close closes the writer; subsequent reads from the read half of the pipe will
|
||||
// return err once the internal buffer get empty.
|
||||
func (w *PipeWriter) CloseWithError(err error) error { |
||||
w.cond.L.Lock() |
||||
defer w.cond.L.Unlock() |
||||
|
||||
if err == nil { |
||||
err = io.EOF |
||||
} |
||||
w.rerr = err |
||||
return nil |
||||
} |
@ -0,0 +1,2 @@ |
||||
// Package bufpipe provides a IO pipe, has variable-sized buffer.
|
||||
package bufpipe |
@ -0,0 +1,5 @@ |
||||
module github.com/acomagu/bufpipe |
||||
|
||||
go 1.12 |
||||
|
||||
require github.com/matryer/is v1.2.0 |
@ -0,0 +1,2 @@ |
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= |
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= |
@ -0,0 +1,48 @@ |
||||
package a |
||||
|
||||
import ( |
||||
. "github.com/alecthomas/chroma" // nolint
|
||||
"github.com/alecthomas/chroma/lexers/internal" |
||||
) |
||||
|
||||
// Al lexer.
|
||||
var Al = internal.Register(MustNewLazyLexer( |
||||
&Config{ |
||||
Name: "AL", |
||||
Aliases: []string{"al"}, |
||||
Filenames: []string{"*.al", "*.dal"}, |
||||
MimeTypes: []string{"text/x-al"}, |
||||
DotAll: true, |
||||
CaseInsensitive: true, |
||||
}, |
||||
alRules, |
||||
)) |
||||
|
||||
// https://github.com/microsoft/AL/blob/master/grammar/alsyntax.tmlanguage
|
||||
func alRules() Rules { |
||||
return Rules{ |
||||
"root": { |
||||
{`\s+`, TextWhitespace, nil}, |
||||
{`(?s)\/\*.*?\\*\*\/`, CommentMultiline, nil}, |
||||
{`(?s)//.*?\n`, CommentSingle, nil}, |
||||
{`\"([^\"])*\"`, Text, nil}, |
||||
{`'([^'])*'`, LiteralString, nil}, |
||||
{`\b(?i:(ARRAY|ASSERTERROR|BEGIN|BREAK|CASE|DO|DOWNTO|ELSE|END|EVENT|EXIT|FOR|FOREACH|FUNCTION|IF|IMPLEMENTS|IN|INDATASET|INTERFACE|INTERNAL|LOCAL|OF|PROCEDURE|PROGRAM|PROTECTED|REPEAT|RUNONCLIENT|SECURITYFILTERING|SUPPRESSDISPOSE|TEMPORARY|THEN|TO|TRIGGER|UNTIL|VAR|WHILE|WITH|WITHEVENTS))\b`, Keyword, nil}, |
||||
{`\b(?i:(AND|DIV|MOD|NOT|OR|XOR))\b`, OperatorWord, nil}, |
||||
{`\b(?i:(AVERAGE|CONST|COUNT|EXIST|FIELD|FILTER|LOOKUP|MAX|MIN|ORDER|SORTING|SUM|TABLEDATA|UPPERLIMIT|WHERE|ASCENDING|DESCENDING))\b`, Keyword, nil}, |
||||
// Added new objects types of BC 2021 wave 1 (REPORTEXTENSION|Entitlement|PermissionSet|PermissionSetExtension)
|
||||
{`\b(?i:(CODEUNIT|PAGE|PAGEEXTENSION|PAGECUSTOMIZATION|DOTNET|ENUM|ENUMEXTENSION|VALUE|QUERY|REPORT|TABLE|TABLEEXTENSION|XMLPORT|PROFILE|CONTROLADDIN|REPORTEXTENSION|Entitlement|PermissionSet|PermissionSetExtension))\b`, Keyword, nil}, |
||||
{`\b(?i:(Action|Array|Automation|BigInteger|BigText|Blob|Boolean|Byte|Char|ClientType|Code|Codeunit|CompletionTriggerErrorLevel|ConnectionType|Database|DataClassification|DataScope|Date|DateFormula|DateTime|Decimal|DefaultLayout|Dialog|Dictionary|DotNet|DotNetAssembly|DotNetTypeDeclaration|Duration|Enum|ErrorInfo|ErrorType|ExecutionContext|ExecutionMode|FieldClass|FieldRef|FieldType|File|FilterPageBuilder|Guid|InStream|Integer|Joker|KeyRef|List|ModuleDependencyInfo|ModuleInfo|None|Notification|NotificationScope|ObjectType|Option|OutStream|Page|PageResult|Query|Record|RecordId|RecordRef|Report|ReportFormat|SecurityFilter|SecurityFiltering|Table|TableConnectionType|TableFilter|TestAction|TestField|TestFilterField|TestPage|TestPermissions|TestRequestPage|Text|TextBuilder|TextConst|TextEncoding|Time|TransactionModel|TransactionType|Variant|Verbosity|Version|XmlPort|HttpContent|HttpHeaders|HttpClient|HttpRequestMessage|HttpResponseMessage|JsonToken|JsonValue|JsonArray|JsonObject|View|Views|XmlAttribute|XmlAttributeCollection|XmlComment|XmlCData|XmlDeclaration|XmlDocument|XmlDocumentType|XmlElement|XmlNamespaceManager|XmlNameTable|XmlNode|XmlNodeList|XmlProcessingInstruction|XmlReadOptions|XmlText|XmlWriteOptions|WebServiceActionContext|WebServiceActionResultCode|SessionSettings))\b`, Keyword, nil}, |
||||
{`\b([<>]=|<>|<|>)\b?`, Operator, nil}, |
||||
{`\b(\-|\+|\/|\*)\b`, Operator, nil}, |
||||
{`\s*(\:=|\+=|-=|\/=|\*=)\s*?`, Operator, nil}, |
||||
{`\b(?i:(ADDFIRST|ADDLAST|ADDAFTER|ADDBEFORE|ACTION|ACTIONS|AREA|ASSEMBLY|CHARTPART|CUEGROUP|CUSTOMIZES|COLUMN|DATAITEM|DATASET|ELEMENTS|EXTENDS|FIELD|FIELDGROUP|FIELDATTRIBUTE|FIELDELEMENT|FIELDGROUPS|FIELDS|FILTER|FIXED|GRID|GROUP|MOVEAFTER|MOVEBEFORE|KEY|KEYS|LABEL|LABELS|LAYOUT|MODIFY|MOVEFIRST|MOVELAST|MOVEBEFORE|MOVEAFTER|PART|REPEATER|USERCONTROL|REQUESTPAGE|SCHEMA|SEPARATOR|SYSTEMPART|TABLEELEMENT|TEXTATTRIBUTE|TEXTELEMENT|TYPE))\b`, Keyword, nil}, |
||||
{`\s*[(\.\.)&\|]\s*`, Operator, nil}, |
||||
{`\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\b`, LiteralNumber, nil}, |
||||
{`[;:,]`, Punctuation, nil}, |
||||
{`#[ \t]*(if|else|elif|endif|define|undef|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil}, |
||||
{`\w+`, Text, nil}, |
||||
{`.`, Text, nil}, |
||||
}, |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue