mirror of https://github.com/perkeep/perkeep.git
Merge branch 'master' of danga.com:camlistore
This commit is contained in:
commit
c323cb2ce5
|
@ -1,11 +1,13 @@
|
|||
all:
|
||||
make -C auth install
|
||||
make -C http_util install
|
||||
make -C openpgp
|
||||
make -C blobserver
|
||||
make -C sigserver
|
||||
|
||||
clean:
|
||||
make -C auth clean
|
||||
make -C http_util clean
|
||||
make -C openpgp clean
|
||||
make -C blobserver clean
|
||||
make -C sigserver clean
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
all:
|
||||
make -C armor install
|
||||
make -C error install
|
||||
make -C packet install
|
||||
|
||||
clean:
|
||||
make -C armor clean
|
||||
make -C error clean
|
||||
make -C packet clean
|
|
@ -0,0 +1,13 @@
|
|||
# Copyright 2009 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.
|
||||
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=crypto/openpgp/armor
|
||||
GOFILES=\
|
||||
armor.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
// 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.
|
||||
|
||||
// This package 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 (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type Block struct {
|
||||
Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE").
|
||||
Headers map[string]string // Optional headers.
|
||||
Bytes []byte // The decoded bytes of the contents.
|
||||
}
|
||||
|
||||
// getLine results the first \r\n or \n delineated line from the given byte
|
||||
// array. The line does not include the \r\n or \n. The remainder of the byte
|
||||
// array (also not including the new line bytes) is also returned and this will
|
||||
// always be smaller than the original argument.
|
||||
func getLine(data []byte) (line, rest []byte) {
|
||||
i := bytes.Index(data, []byte{'\n'})
|
||||
var j int
|
||||
if i < 0 {
|
||||
i = len(data)
|
||||
j = i
|
||||
} else {
|
||||
j = i + 1
|
||||
if i > 0 && data[i-1] == '\r' {
|
||||
i--
|
||||
}
|
||||
}
|
||||
return data[0:i], data[j:]
|
||||
}
|
||||
|
||||
// removeWhitespace returns a copy of its input with all spaces, tab and
|
||||
// newline characters removed.
|
||||
func removeWhitespace(data []byte) []byte {
|
||||
result := make([]byte, len(data))
|
||||
n := 0
|
||||
|
||||
for _, b := range data {
|
||||
if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
|
||||
continue
|
||||
}
|
||||
result[n] = b
|
||||
n++
|
||||
}
|
||||
|
||||
return result[0:n]
|
||||
}
|
||||
|
||||
const crc24Init = 0xb704ce
|
||||
const crc24Poly = 0x1864cfb
|
||||
|
||||
// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
|
||||
func crc24(d []byte) uint32 {
|
||||
crc := uint64(crc24Init)
|
||||
for _, b := range d {
|
||||
crc ^= uint64(b) << 16
|
||||
for i := 0; i < 8; i++ {
|
||||
crc <<= 1
|
||||
if crc&0x1000000 != 0 {
|
||||
crc ^= crc24Poly
|
||||
}
|
||||
}
|
||||
}
|
||||
return uint32(crc & 0xffffff)
|
||||
}
|
||||
|
||||
var armorStart = []byte("\n-----BEGIN ")
|
||||
var armorEnd = []byte("\n-----END ")
|
||||
var armorEndOfLine = []byte("-----")
|
||||
|
||||
// Decode will find the next armored block in the input. It returns that block
|
||||
// and the remainder of the input. If no block is found, p is nil and the whole
|
||||
// of the input is returned in rest.
|
||||
func Decode(data []byte) (p *Block, rest []byte) {
|
||||
// armorStart begins with a newline. However, at the very beginning of
|
||||
// the byte array, we'll accept the start string without it.
|
||||
rest = data
|
||||
if bytes.HasPrefix(data, armorStart[1:]) {
|
||||
rest = rest[len(armorStart)-1 : len(data)]
|
||||
} else if i := bytes.Index(data, armorStart); i >= 0 {
|
||||
rest = rest[i+len(armorStart) : len(data)]
|
||||
} else {
|
||||
return nil, data
|
||||
}
|
||||
|
||||
typeLine, rest := getLine(rest)
|
||||
if !bytes.HasSuffix(typeLine, armorEndOfLine) {
|
||||
goto Error
|
||||
}
|
||||
typeLine = typeLine[0 : len(typeLine)-len(armorEndOfLine)]
|
||||
|
||||
p = &Block{
|
||||
Headers: make(map[string]string),
|
||||
Type: string(typeLine),
|
||||
}
|
||||
|
||||
for {
|
||||
// This loop terminates because getLine's second result is
|
||||
// always smaller than it's argument.
|
||||
if len(rest) == 0 {
|
||||
return nil, data
|
||||
}
|
||||
line, next := getLine(rest)
|
||||
|
||||
i := bytes.Index(line, []byte{':'})
|
||||
if i == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(agl): need to cope with values that spread across lines.
|
||||
key, val := line[0:i], line[i+1:]
|
||||
key = bytes.TrimSpace(key)
|
||||
val = bytes.TrimSpace(val)
|
||||
p.Headers[string(key)] = string(val)
|
||||
rest = next
|
||||
}
|
||||
|
||||
i := bytes.Index(rest, armorEnd)
|
||||
if i < 4 {
|
||||
print("1\n")
|
||||
goto Error
|
||||
}
|
||||
encodedChecksumLength := 5
|
||||
if rest[i-1] == '\r' {
|
||||
print("2\n")
|
||||
encodedChecksumLength = 6
|
||||
}
|
||||
encodedChecksum := removeWhitespace(rest[i-encodedChecksumLength : i])
|
||||
if encodedChecksum[0] != '=' {
|
||||
goto Error
|
||||
}
|
||||
encodedChecksum = encodedChecksum[1:]
|
||||
checksumBytes := make([]byte, base64.StdEncoding.DecodedLen(len(encodedChecksum)))
|
||||
n, err := base64.StdEncoding.Decode(checksumBytes, encodedChecksum)
|
||||
if err != nil || n != 3 {
|
||||
goto Error
|
||||
}
|
||||
checksum := uint32(checksumBytes[0])<<16 |
|
||||
uint32(checksumBytes[1])<<8 |
|
||||
uint32(checksumBytes[2])
|
||||
|
||||
base64Data := removeWhitespace(rest[0 : i-encodedChecksumLength])
|
||||
|
||||
p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
|
||||
n, err = base64.StdEncoding.Decode(p.Bytes, base64Data)
|
||||
if err != nil {
|
||||
goto Error
|
||||
}
|
||||
p.Bytes = p.Bytes[0:n]
|
||||
|
||||
calculatedChecksum := crc24(p.Bytes)
|
||||
if calculatedChecksum != checksum {
|
||||
print("foo ", calculatedChecksum, " ", checksum, "\n")
|
||||
goto Error
|
||||
}
|
||||
|
||||
p.Bytes = p.Bytes[0:n]
|
||||
|
||||
_, rest = getLine(rest[i+len(armorEnd):])
|
||||
|
||||
return
|
||||
|
||||
Error:
|
||||
// If we get here then we have rejected a likely looking, but
|
||||
// ultimately invalid block. We need to start over from a new
|
||||
// position. We have consumed the preamble line and will have consumed
|
||||
// any lines which could be header lines. However, a valid preamble
|
||||
// line is not a valid header line, therefore we cannot have consumed
|
||||
// the preamble line for the any subsequent block. Thus, we will always
|
||||
// find any valid block, no matter what bytes preceed it.
|
||||
//
|
||||
// For example, if the input is
|
||||
//
|
||||
// -----BEGIN MALFORMED BLOCK-----
|
||||
// junk that may look like header lines
|
||||
// or data lines, but no END line
|
||||
//
|
||||
// -----BEGIN ACTUAL BLOCK-----
|
||||
// realdata
|
||||
// -----END ACTUAL BLOCK-----
|
||||
//
|
||||
// we've failed to parse using the first BEGIN line
|
||||
// and now will try again, using the second BEGIN line.
|
||||
p, rest = Decode(rest)
|
||||
if p == nil {
|
||||
rest = data
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// 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 (
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type GetLineTest struct {
|
||||
in, out1, out2 string
|
||||
}
|
||||
|
||||
var getLineTests = []GetLineTest{
|
||||
GetLineTest{"abc", "abc", ""},
|
||||
GetLineTest{"abc\r", "abc\r", ""},
|
||||
GetLineTest{"abc\n", "abc", ""},
|
||||
GetLineTest{"abc\r\n", "abc", ""},
|
||||
GetLineTest{"abc\nd", "abc", "d"},
|
||||
GetLineTest{"abc\r\nd", "abc", "d"},
|
||||
GetLineTest{"\nabc", "", "abc"},
|
||||
GetLineTest{"\r\nabc", "", "abc"},
|
||||
}
|
||||
|
||||
func TestGetLine(t *testing.T) {
|
||||
for i, test := range getLineTests {
|
||||
x, y := getLine([]byte(test.in))
|
||||
if string(x) != test.out1 || string(y) != test.out2 {
|
||||
t.Errorf("#%d got:%+v,%+v want:%s,%s", i, x, y, test.out1, test.out2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
result, _ := Decode([]byte(armorExample1))
|
||||
fmt.Printf("%#v\n", result)
|
||||
}
|
||||
|
||||
const armorExample1 = `-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.10 (GNU/Linux)
|
||||
|
||||
iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8
|
||||
kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp
|
||||
cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA
|
||||
byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3
|
||||
WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv
|
||||
okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4=
|
||||
=wfQG
|
||||
-----END PGP SIGNATURE-----`
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2009 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.
|
||||
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=crypto/openpgp/error
|
||||
GOFILES=\
|
||||
error.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package error
|
||||
|
||||
type StructuralError string
|
||||
|
||||
func (s StructuralError) String() string {
|
||||
return "OpenPGP data invalid: " + string(s)
|
||||
}
|
||||
|
||||
type Unsupported string
|
||||
|
||||
func (s Unsupported) String() string {
|
||||
return "OpenPGP feature unsupported: " + string(s)
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/openpgp/armor"
|
||||
"crypto/openpgp/packet"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
func readOpenPGPPacketFromArmoredFileOrDie(fileName string, armorType string) (p packet.Packet) {
|
||||
data, err := ioutil.ReadFile(fileName)
|
||||
if err != nil {
|
||||
log.Exit("Cannot open '%s': %s", fileName, err)
|
||||
}
|
||||
|
||||
block, _ := armor.Decode(data)
|
||||
if block == nil {
|
||||
log.Exit("cannot parse armor")
|
||||
}
|
||||
if block.Type != armorType {
|
||||
log.Exitf("bad type in '%s' (got: %s, want: %s)", fileName, block.Type, armorType)
|
||||
}
|
||||
buf := bytes.NewBuffer(block.Bytes)
|
||||
p, err = packet.ReadPacket(buf)
|
||||
if err != nil {
|
||||
log.Exitf("failed to parse packet from '%s': %s", fileName, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
signedData, err := ioutil.ReadFile("signed-file")
|
||||
if err != nil {
|
||||
log.Exitf("Cannot open 'signed-file': %s", err)
|
||||
}
|
||||
|
||||
p := readOpenPGPPacketFromArmoredFileOrDie("public-key", "PGP PUBLIC KEY BLOCK")
|
||||
pk, ok := p.(packet.PublicKeyPacket)
|
||||
if !ok {
|
||||
log.Exit("didn't find a public key in the public key file")
|
||||
}
|
||||
|
||||
p = readOpenPGPPacketFromArmoredFileOrDie("signed-file.asc", "PGP SIGNATURE")
|
||||
sig, ok := p.(packet.SignaturePacket)
|
||||
if !ok {
|
||||
log.Exit("didn't find a signature in the signature file")
|
||||
}
|
||||
|
||||
if sig.Hash != packet.HashFuncSHA1 {
|
||||
log.Exit("I only do SHA1")
|
||||
}
|
||||
if sig.SigType != packet.SigTypeBinary {
|
||||
log.Exit("I only do binary signatures")
|
||||
}
|
||||
|
||||
hash := sha1.New()
|
||||
hash.Write(signedData)
|
||||
hash.Write(sig.HashSuffix)
|
||||
hashBytes := hash.Sum()
|
||||
|
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
||||
log.Exit("hash tag doesn't match")
|
||||
}
|
||||
|
||||
err = rsa.VerifyPKCS1v15(&pk.PublicKey, rsa.HashSHA1, hashBytes, sig.Signature)
|
||||
if err != nil {
|
||||
log.Exitf("bad signature: %s", err)
|
||||
}
|
||||
|
||||
log.Print("good signature")
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2009 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.
|
||||
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=crypto/openpgp/packet
|
||||
GOFILES=\
|
||||
packet.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"big"
|
||||
"crypto/openpgp/error"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func readHeader(r io.Reader) (tag uint8, length uint64, err os.Error) {
|
||||
var buf [4]byte
|
||||
_, err = io.ReadFull(r, buf[0:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] & 0x80 == 0 {
|
||||
err = error.StructuralError("tag byte does not have MSB set")
|
||||
return
|
||||
}
|
||||
if buf[0] & 0x40 == 0 {
|
||||
// Old format packet
|
||||
tag = (buf[0] & 0x3f) >> 2
|
||||
lengthType := buf[0] & 3
|
||||
if lengthType == 3 {
|
||||
err = error.Unsupported("indeterminate length packet")
|
||||
return
|
||||
}
|
||||
lengthBytes := 1 << lengthType
|
||||
_, err = io.ReadFull(r, buf[0:lengthBytes])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < lengthBytes; i++ {
|
||||
length <<= 8
|
||||
length |= uint64(buf[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// New format packet
|
||||
tag = buf[0] & 0x3f
|
||||
_, err = io.ReadFull(r, buf[0:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case buf[0] < 192:
|
||||
length = uint64(buf[0])
|
||||
case buf[0] < 224:
|
||||
length = uint64(buf[0] - 192) << 8
|
||||
_, err = io.ReadFull(r, buf[0:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length += uint64(buf[0]) + 192
|
||||
case buf[0] < 255:
|
||||
err = error.Unsupported("chunked packet")
|
||||
default:
|
||||
_, err := io.ReadFull(r, buf[0:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length = uint64(buf[0]) << 24 |
|
||||
uint64(buf[1]) << 16 |
|
||||
uint64(buf[2]) << 8 |
|
||||
uint64(buf[3])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Packet interface {
|
||||
Type() string
|
||||
}
|
||||
|
||||
func ReadPacket(r io.Reader) (p Packet, err os.Error) {
|
||||
tag, length, err := readHeader(r)
|
||||
limitReader := io.LimitReader(r, int64(length))
|
||||
switch tag {
|
||||
case 2:
|
||||
p, err = readSignaturePacket(limitReader)
|
||||
case 6:
|
||||
p, err = readPublicKeyPacket(limitReader)
|
||||
default:
|
||||
err = error.Unsupported("unknown packet type")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type SignatureType uint8
|
||||
type PublicKeyAlgorithm uint8
|
||||
type HashFunction uint8
|
||||
|
||||
const (
|
||||
SigTypeBinary SignatureType = 0
|
||||
SigTypeText SignatureType = 1
|
||||
// Many other types omitted
|
||||
)
|
||||
|
||||
const (
|
||||
// RFC 4880, section 9.1
|
||||
PubKeyAlgoRSA PublicKeyAlgorithm = 1
|
||||
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
|
||||
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
|
||||
PubKeyAlgoElgamal PublicKeyAlgorithm = 16
|
||||
PubKeyAlgoDSA PublicKeyAlgorithm = 17
|
||||
)
|
||||
|
||||
const (
|
||||
// RFC 4880, section 9.4
|
||||
HashFuncSHA1 = 2
|
||||
)
|
||||
|
||||
type SignaturePacket struct {
|
||||
SigType SignatureType
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
Hash HashFunction
|
||||
HashSuffix []byte
|
||||
HashTag [2]byte
|
||||
CreationTime uint32
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
func (s SignaturePacket) Type() string {
|
||||
return "signature"
|
||||
}
|
||||
|
||||
func readSignaturePacket(r io.Reader) (sig SignaturePacket, err os.Error) {
|
||||
// RFC 4880, section 5.2.3
|
||||
var buf [5]byte
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 4 {
|
||||
err = error.Unsupported("signature packet version")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(r, buf[:5])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sig.SigType = SignatureType(buf[0])
|
||||
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
|
||||
switch sig.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
default:
|
||||
err = error.Unsupported("public key algorithm")
|
||||
return
|
||||
}
|
||||
sig.Hash = HashFunction(buf[2])
|
||||
hashedSubpacketsLength := int(buf[3]) << 8 | int(buf[4])
|
||||
l := 6 + hashedSubpacketsLength
|
||||
sig.HashSuffix = make([]byte, l + 6)
|
||||
sig.HashSuffix[0] = 4
|
||||
copy(sig.HashSuffix[1:], buf[:5])
|
||||
hashedSubpackets := sig.HashSuffix[6:l]
|
||||
_, err = io.ReadFull(r, hashedSubpackets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// See RFC 4880, section 5.2.4
|
||||
trailer := sig.HashSuffix[l:]
|
||||
trailer[0] = 4
|
||||
trailer[1] = 0xff
|
||||
trailer[2] = uint8(l >> 24)
|
||||
trailer[3] = uint8(l >> 16)
|
||||
trailer[4] = uint8(l >> 8)
|
||||
trailer[5] = uint8(l)
|
||||
|
||||
err = parseSignatureSubpackets(&sig, hashedSubpackets, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(r, buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
unhashedSubpacketsLength := int(buf[0]) << 8 | int(buf[1])
|
||||
unhashedSubpackets := make([]byte, unhashedSubpacketsLength)
|
||||
_, err = io.ReadFull(r, unhashedSubpackets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = parseSignatureSubpackets(&sig, unhashedSubpackets, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(r, sig.HashTag[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// We have already checked that the public key algorithm is RSA.
|
||||
sig.Signature, err = readMPI(r)
|
||||
return
|
||||
}
|
||||
|
||||
func readMPI(r io.Reader) (mpi []byte, err os.Error) {
|
||||
var buf [2]byte
|
||||
_, err = io.ReadFull(r, buf[0:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
numBits := int(buf[0]) << 8 | int(buf[1])
|
||||
numBytes := (numBits + 7) / 8
|
||||
mpi = make([]byte, numBytes)
|
||||
_, err = io.ReadFull(r, mpi)
|
||||
return
|
||||
}
|
||||
|
||||
func parseSignatureSubpackets(sig *SignaturePacket, subpackets []byte, isHashed bool) (err os.Error) {
|
||||
for len(subpackets) > 0 {
|
||||
subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if sig.CreationTime == 0 {
|
||||
err = error.StructuralError("no creation time in signature")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseSignatureSubpacket(sig *SignaturePacket, subpacket []byte, isHashed bool) (rest []byte, err os.Error) {
|
||||
// RFC 4880, section 5.2.3.1
|
||||
var length uint32
|
||||
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 = error.StructuralError("zero length signature subpacket")
|
||||
return
|
||||
}
|
||||
packetType := subpacket[0] & 0x7f
|
||||
isCritial := subpacket[0] & 0x80 == 0x80
|
||||
subpacket = subpacket[1:]
|
||||
switch packetType {
|
||||
case 2:
|
||||
if !isHashed {
|
||||
err = error.StructuralError("signature creation time in non-hashed area")
|
||||
return
|
||||
}
|
||||
if len(subpacket) != 4 {
|
||||
err = error.StructuralError("signature creation time not four bytes")
|
||||
return
|
||||
}
|
||||
sig.CreationTime = uint32(subpacket[0]) << 24 |
|
||||
uint32(subpacket[1]) << 16 |
|
||||
uint32(subpacket[2]) << 8 |
|
||||
uint32(subpacket[3])
|
||||
default:
|
||||
if isCritial {
|
||||
err = error.Unsupported("unknown critical signature subpacket")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
Truncated:
|
||||
err = error.StructuralError("signature subpacket truncated")
|
||||
return
|
||||
}
|
||||
|
||||
type PublicKeyPacket struct {
|
||||
CreationTime uint32
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
PublicKey rsa.PublicKey
|
||||
}
|
||||
|
||||
func (pk PublicKeyPacket) Type() string {
|
||||
return "public key"
|
||||
}
|
||||
|
||||
func readPublicKeyPacket(r io.Reader) (pk PublicKeyPacket, err os.Error) {
|
||||
// RFC 4880, section 5.5.2
|
||||
var buf [6]byte
|
||||
_, err = io.ReadFull(r, buf[0:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 4 {
|
||||
err = error.Unsupported("public key version")
|
||||
}
|
||||
pk.CreationTime = uint32(buf[1]) << 24 |
|
||||
uint32(buf[2]) << 16 |
|
||||
uint32(buf[3]) << 8 |
|
||||
uint32(buf[4])
|
||||
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
default:
|
||||
err = error.Unsupported("public key type")
|
||||
return
|
||||
}
|
||||
nBytes, err := readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
eBytes, err := readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(eBytes) > 3 {
|
||||
err = error.Unsupported("large public exponent")
|
||||
return
|
||||
}
|
||||
pk.PublicKey.E = 0
|
||||
for i := 0; i < len(eBytes); i++ {
|
||||
pk.PublicKey.E <<= 8
|
||||
pk.PublicKey.E |= int(eBytes[i])
|
||||
}
|
||||
pk.PublicKey.N = (new(big.Int)).SetBytes(nBytes)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func TestSignatureRead(t *testing.T) {
|
||||
signatureData, _ := hex.DecodeString(signatureDataHex)
|
||||
buf := bytes.NewBuffer(signatureData)
|
||||
packet, err := ReadPacket(buf)
|
||||
fmt.Printf("%#v, %s\n", packet, err)
|
||||
}
|
||||
|
||||
func TestPublicKeyRead(t *testing.T) {
|
||||
pkData, _ := hex.DecodeString(pkDataHex)
|
||||
buf := bytes.NewBuffer(pkData)
|
||||
packet, err := ReadPacket(buf)
|
||||
fmt.Printf("%#v, %s\n", packet, err)
|
||||
}
|
||||
|
||||
const signatureDataHex = "89011c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e"
|
||||
|
||||
const pkDataHex = "99010d044ba02c9a010800b043c0fc9d7881a97e3cf479629acfbdf3f9c68efc838c688da245627ec7ab45aeb089fbb7b1a1a05ef541668edf919f284c96639375ac30504a850cc58e371bdae602252b9014d6f2da5a228a1da46a8e6320efd08adba4ac62be949e3976b3bc504574ebf95bbe238b7851875772beb80d21b0d1ae44a8b5530e2ab63b072c57fe6dfcb5dc575f4882492a64b60607018f7a82c4d6a50c35ef682eff83b2ae412556349a5365e5a5233b42ef003238869e383e15ed6952260e1cb9c54d1f8d884044365fea24c31607a7604aea1a713d9d7962ad46563302639954a5b13c84f0b94eb418f26c0916c211d8f5b46c527c5b9fa4d574dbff222c06d73cee5a0d0011010001b41c54657374204b6579203c66616c7365406578616d706c652e636f6d3e89013804130102002205024ba02c9a021b03060b090807030206150802090a0b0416020301021e01021780000a0910ab105c91af38fb1548c507ff79bef8990a3cbfa3553ffbf8929b52b2b6cfe5b5b8ed974ae3c199e90e4f4b77665317212891ce106100b080e65f5967ed29faa6e105f4b91eed70784653b80da51f1771deb86f133568c2da1de552fc9dbf0efa3be40fb53e8c29778f69844b0c9cd32b682fd680722b6f3502ded5ee6da4335ea83bbed1322e081fa9de500bad3d004a6c260be4a8e772fe1a4ead85493a4bc2db5b25a2992833016a83560aaec50b28dbb792ad1f9ac53f18db8c3f896bbd3c4087bd681754e2cf87f9bb2a28ca443b69119c1f6740caa780fe920670df183584f718fcfbfda7dedc0f3e329b764d0bc6dda5398f60b328875ab1ad8efb72515abde2a366dc3c7d242d949b"
|
|
@ -0,0 +1,18 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.10 (GNU/Linux)
|
||||
|
||||
mQENBEugLJoBCACwQ8D8nXiBqX489Hlims+98/nGjvyDjGiNokVifserRa6wifu3
|
||||
saGgXvVBZo7fkZ8oTJZjk3WsMFBKhQzFjjcb2uYCJSuQFNby2loiih2kao5jIO/Q
|
||||
itukrGK+lJ45drO8UEV06/lbviOLeFGHV3K+uA0hsNGuRKi1Uw4qtjsHLFf+bfy1
|
||||
3FdfSIJJKmS2BgcBj3qCxNalDDXvaC7/g7KuQSVWNJpTZeWlIztC7wAyOIaeOD4V
|
||||
7WlSJg4cucVNH42IQEQ2X+okwxYHp2BK6hpxPZ15Yq1GVjMCY5lUpbE8hPC5TrQY
|
||||
8mwJFsIR2PW0bFJ8W5+k1XTb/yIsBtc87loNABEBAAG0HFRlc3QgS2V5IDxmYWxz
|
||||
ZUBleGFtcGxlLmNvbT6JATgEEwECACIFAkugLJoCGwMGCwkIBwMCBhUIAgkKCwQW
|
||||
AgMBAh4BAheAAAoJEKsQXJGvOPsVSMUH/3m++JkKPL+jVT/7+JKbUrK2z+W1uO2X
|
||||
SuPBmekOT0t3ZlMXISiRzhBhALCA5l9ZZ+0p+qbhBfS5Hu1weEZTuA2lHxdx3rhv
|
||||
EzVowtod5VL8nb8O+jvkD7U+jCl3j2mESwyc0ytoL9aAcitvNQLe1e5tpDNeqDu+
|
||||
0TIuCB+p3lALrT0ASmwmC+So53L+Gk6thUk6S8LbWyWimSgzAWqDVgquxQso27eS
|
||||
rR+axT8Y24w/iWu9PECHvWgXVOLPh/m7KijKRDtpEZwfZ0DKp4D+kgZw3xg1hPcY
|
||||
/Pv9p97cDz4ym3ZNC8bdpTmPYLMoh1qxrY77clFaveKjZtw8fSQtlJs=
|
||||
=K51W
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1 @@
|
|||
Hello Brad.
|
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.10 (GNU/Linux)
|
||||
|
||||
iQEcBAABAgAGBQJMtH2SAAoJEKsQXJGvOPsVJgkH/2Y93lc17iUWdPxgdQuD85tH
|
||||
2uzr26Hd7XEdxGwySngDf9/Bd4iQpPTMynumpZePUiE/cXrWULgpBamznYPCoRxH
|
||||
tjKHQrXJ/ENntx/m1gViETg5TbH/KS72eJIm20PmDDsvkvfIFQCnDjQmbgvj2RO2
|
||||
vOsWF3IOyO+70C+dCXbdd7Qev/00Rdr40Y4n1VZq6QoC37QsNFlpczZNrQjQCNZj
|
||||
0Xnw7BnVIOjQnl1mGYcFJlOFGVwUGnj0TkWYPeXtPA39/cWklXInlgaUsFxtUSmB
|
||||
sq12AY9G4wMOSPlmxtMegkhGScSvFsRtGPLsP8IK127u1EDeDohxM+2mzmtYR+Y=
|
||||
=4uTw
|
||||
-----END PGP SIGNATURE-----
|
Loading…
Reference in New Issue