Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is much more structured, easier to follow, and the individual parts are more composable.
- Loading branch information
Showing
7 changed files
with
795 additions
and
576 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"encoding/base64" | ||
"io" | ||
) | ||
|
||
// Armor returns the ASCII armored version of its input packet. It | ||
// autodetects what kind of armor should be used based on the packet | ||
// header. | ||
func Armor(buf []byte) []byte { | ||
const ( | ||
pubBeg = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n" | ||
pubEnd = "\n-----END PGP PUBLIC KEY BLOCK-----\n" | ||
secBeg = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n\n" | ||
secEnd = "\n-----END PGP PRIVATE KEY BLOCK-----\n" | ||
sigBeg = "-----BEGIN PGP SIGNATURE-----\n\n" | ||
sigEnd = "\n-----END PGP SIGNATURE-----\n" | ||
) | ||
|
||
var beg, end string | ||
switch buf[0] { | ||
case 0xc0 | 2: | ||
beg = sigBeg | ||
end = sigEnd | ||
case 0xc0 | 5: | ||
beg = secBeg | ||
end = secEnd | ||
case 0xc0 | 6: | ||
beg = pubBeg | ||
end = pubEnd | ||
} | ||
|
||
var asc bytes.Buffer | ||
asc.WriteString(beg) | ||
wrap := &wrapper{&asc, 64, 0} | ||
b64 := base64.NewEncoder(base64.RawStdEncoding, wrap) | ||
b64.Write(buf) | ||
b64.Close() | ||
asc.WriteString("\n=") | ||
b64 = base64.NewEncoder(base64.RawStdEncoding, &asc) | ||
crc := crc24(buf) | ||
b64.Write([]byte{byte(crc >> 16), byte(crc >> 8), byte(crc)}) | ||
b64.Close() | ||
asc.WriteString(end) | ||
return asc.Bytes() | ||
} | ||
|
||
// Return CRC-24 checksum for a buffer. | ||
func crc24(buf []byte) int32 { | ||
const ( | ||
crc24Init = 0x0b704ce | ||
crc24Poly = 0x1864cfb | ||
) | ||
var crc int32 = crc24Init | ||
for _, b := range buf { | ||
crc ^= int32(b) << 16 | ||
for i := 0; i < 8; i++ { | ||
crc <<= 1 | ||
if crc&0x1000000 != 0 { | ||
crc ^= crc24Poly | ||
} | ||
} | ||
} | ||
return crc & 0xFFFFFF | ||
} | ||
|
||
// wrapper is an io.Writer filter that inserts regular hard line breaks. | ||
type wrapper struct { | ||
w io.Writer | ||
max int | ||
count int | ||
} | ||
|
||
func (w *wrapper) Write(p []byte) (int, error) { | ||
for len(p) > 0 { | ||
if w.count == w.max { | ||
if _, err := w.w.Write([]byte{10}); err != nil { | ||
return 0, err | ||
} | ||
w.count = 0 | ||
} | ||
left := w.max - w.count | ||
var line []byte | ||
if len(p) > left { | ||
line = p[:left] | ||
} else { | ||
line = p | ||
} | ||
p = p[len(line):] | ||
w.count += len(line) | ||
_, err := w.w.Write(line) | ||
if err != nil { | ||
return 0, err | ||
} | ||
} | ||
return len(p), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/binary" | ||
|
||
"golang.org/x/crypto/curve25519" | ||
) | ||
|
||
const ( | ||
// EncryptKeyPubLen is the size of the public part of an OpenPGP packet. | ||
EncryptKeyPubLen = 58 | ||
) | ||
|
||
// EncryptKey represents an X25519 Diffie-Hellman key (ECDH). Implements | ||
// Bindable. | ||
type EncryptKey struct { | ||
Key []byte | ||
created int64 | ||
packet []byte | ||
} | ||
|
||
// Seed sets the 32-byte seed for a sign key. | ||
func (k *EncryptKey) Seed(seed []byte) { | ||
var pubkey [32]byte | ||
var seckey [32]byte | ||
copy(seckey[:], seed) | ||
seckey[0] &= 248 | ||
seckey[31] &= 127 | ||
seckey[31] |= 64 | ||
curve25519.ScalarBaseMult(&pubkey, &seckey) | ||
k.Key = append(seckey[:], pubkey[:]...) | ||
k.packet = nil | ||
} | ||
|
||
// Created returns the key's creation date in unix epoch seconds. | ||
func (k *EncryptKey) Created() int64 { | ||
return k.created | ||
} | ||
|
||
// SetCreated sets the creation date in unix epoch seconds. | ||
func (k *EncryptKey) SetCreated(time int64) { | ||
k.created = time | ||
k.packet = nil | ||
} | ||
|
||
// Seckey returns the secret key portion of this key. | ||
func (k *EncryptKey) Seckey() []byte { | ||
return k.Key[:32] | ||
} | ||
|
||
// Pubkey returns the public key portion of this key. | ||
func (k *EncryptKey) Pubkey() []byte { | ||
return k.Key[32:] | ||
} | ||
|
||
// Packet returns the OpenPGP packet encoding this key. | ||
func (k *EncryptKey) Packet() []byte { | ||
const encryptKeySecLen = 3 + 32 + 2 | ||
total := EncryptKeyPubLen + encryptKeySecLen | ||
be := binary.BigEndian | ||
|
||
if k.packet != nil { | ||
return k.packet | ||
} | ||
|
||
packet := make([]byte, EncryptKeyPubLen+1, total) | ||
packet[0] = 0xc0 | 7 // packet header, Secret-Subkey Packet (7) | ||
packet[2] = 0x04 // packet version, new (4) | ||
|
||
// Public Key | ||
be.PutUint32(packet[3:7], uint32(k.created)) // creation date | ||
packet[7] = 18 // algorithm, Elliptic Curve | ||
packet[8] = 10 // OID length | ||
// OID (1.3.6.1.4.1.3029.1.5.1) | ||
oid := []byte{0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01} | ||
copy(packet[9:19], oid) | ||
be.PutUint16(packet[19:21], 263) // public key length (always 263 bits) | ||
packet[21] = 0x40 // MPI prefix | ||
copy(packet[22:54], k.Pubkey()) | ||
// KDF parameters | ||
packet[54] = 3 // length | ||
packet[55] = 0x01 // reserved (1) | ||
packet[56] = 0x08 // SHA-256 | ||
packet[57] = 0x07 // AES-128? (spec is incorrect) | ||
|
||
// Secret Key | ||
packet[58] = 0 // string-to-key, unencrypted | ||
// append MPI-encoded key | ||
mpikey := mpi(reverse(k.Seckey())) | ||
packet = append(packet, mpikey...) | ||
// compute and append checksum | ||
var checksum uint16 | ||
for _, b := range mpikey { | ||
checksum += uint16(b) | ||
} | ||
packet = packet[:len(packet)+2] | ||
be.PutUint16(packet[len(packet)-2:], checksum) | ||
|
||
packet[1] = byte(len(packet) - 2) // packet length | ||
k.packet = packet | ||
return packet | ||
} | ||
|
||
// PubPacket returns an OpenPGP public key packet for this key. | ||
func (k *EncryptKey) PubPacket() []byte { | ||
packet := make([]byte, EncryptKeyPubLen) | ||
packet[0] = 0xc0 | 14 // packet header, Public-Subkey packet (14) | ||
packet[1] = EncryptKeyPubLen - 2 | ||
copy(packet[2:], k.Packet()[2:]) | ||
return packet | ||
} | ||
|
||
func (k *EncryptKey) SignType() byte { | ||
return 0x18 | ||
} | ||
|
||
func (k *EncryptKey) SignData() []byte { | ||
prefix := []byte{0x99, 0, 56} | ||
packet := k.PubPacket()[2:] | ||
return append(prefix, packet...) | ||
} | ||
|
||
// Returns a reversed copy of its input. | ||
func reverse(b []byte) []byte { | ||
c := make([]byte, len(b)) | ||
for i, v := range b { | ||
c[len(c)-i-1] = v | ||
} | ||
return c | ||
} |
Oops, something went wrong.