very rough su3 creation and signing

This commit is contained in:
Matt Drollette
2014-12-10 01:10:37 -06:00
parent 2d445337c8
commit a2891a2bc6
11 changed files with 511 additions and 19 deletions

6
.gitignore vendored
View File

@@ -1,5 +1,7 @@
/i2p-reseeder
/cert.pem
/key.pem
/netdb
i2pseeds.su3
/_netdb
i2pseeds.su3
reseed_cert.pem
reseed_private.pem

88
cmd/keygen.go Normal file
View File

@@ -0,0 +1,88 @@
package cmd
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"os"
"time"
"github.com/codegangsta/cli"
)
func NewKeygenCommand() cli.Command {
return cli.Command{
Name: "keygen",
Usage: "Generate keys for reseed Su3 signing",
Description: "Generate keys for reseed Su3 signing",
Action: keygenAction,
Flags: []cli.Flag{
cli.StringFlag{
Name: "signer",
Usage: "Your email address (ex. something@mail.i2p)",
},
},
}
}
func keygenAction(c *cli.Context) {
//"CN=" + cname + ",OU=" + ou + ",O=I2P Anonymous Network,L=XX,ST=XX,C=XX",
template := &x509.Certificate{
BasicConstraintsValid: true,
IsCA: true,
SubjectKeyId: []byte{1, 2, 3},
SerialNumber: big.NewInt(1234),
Subject: pkix.Name{
Organization: []string{"I2P Anonymous Network"},
OrganizationalUnit: []string{"I2P"},
Locality: []string{"XX"},
StreetAddress: []string{"XX"},
Country: []string{"XX"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
}
// generate private key
privatekey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
log.Fatalln(err)
}
publickey := &privatekey.PublicKey
// create a self-signed certificate. template = parent
var parent = template
cert, err := x509.CreateCertificate(rand.Reader, template, parent, publickey, privatekey)
if err != nil {
log.Fatalln(err)
}
// save private key
pemfile, err := os.Create("reseed_private.pem")
if err != nil {
log.Fatalf("failed to open reseed_cert.pem for writing: %s", err)
}
var pemkey = &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privatekey)}
pem.Encode(pemfile, pemkey)
pemfile.Close()
fmt.Println("private key saved to reseed_private.pem")
// save cert
certOut, err := os.Create("reseed_cert.pem")
if err != nil {
log.Fatalf("failed to open reseed_cert.pem for writing: %s", err)
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
certOut.Close()
fmt.Println("certificate saved to reseed_cert.pem")
}

View File

@@ -1,18 +1,20 @@
package cmd
import (
"fmt"
"log"
"net/http"
// "github.com/MDrollette/go-i2p/reseed"
"github.com/MDrollette/go-i2p/reseed"
"github.com/codegangsta/cli"
)
func NewReseederCommand() cli.Command {
func NewReseedCommand() cli.Command {
return cli.Command{
Name: "reseeder",
Name: "reseed",
Usage: "Start a reseed server",
Description: "Start a reseed server",
Action: reseederAction,
Action: reseedAction,
Flags: []cli.Flag{
cli.StringFlag{
Name: "addr",
@@ -23,6 +25,24 @@ func NewReseederCommand() cli.Command {
}
}
func reseederAction(c *cli.Context) {
func reseedAction(c *cli.Context) {
log.Println("Starting server on", c.String("addr"))
netdb := reseed.NewLocalNetDb(c.Args().Get(0))
reseeder := reseed.NewReseeder(netdb)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
peer := reseeder.Peer(r)
seeds, err := reseeder.Seed(peer)
if nil != err {
fmt.Fprintf(w, "Problem: '%s'", err)
return
}
for _, s := range seeds {
fmt.Fprintf(w, "%s\n", s.Name)
}
})
http.ListenAndServe("127.0.0.1:9090", nil)
}

60
cmd/su3.go Normal file
View File

@@ -0,0 +1,60 @@
package cmd
import (
"crypto/x509"
"encoding/pem"
"io/ioutil"
"log"
"net/http"
"github.com/MDrollette/go-i2p/reseed"
"github.com/MDrollette/go-i2p/su3"
"github.com/codegangsta/cli"
)
func NewSu3Command() cli.Command {
return cli.Command{
Name: "su3",
Usage: "Do SU3 things",
Description: "Do SU3 things",
Action: su3Action,
Flags: []cli.Flag{},
}
}
func su3Action(c *cli.Context) {
netdb := reseed.NewLocalNetDb(c.Args().Get(0))
reseeder := reseed.NewReseeder(netdb)
// make a fake request to get a peer
r, _ := http.NewRequest("GET", "/i2pseeds.su3", nil)
peer := reseeder.Peer(r)
seeds, err := reseeder.Seed(peer)
if nil != err {
log.Fatalln(err)
return
}
// load our signing privKey
privPem, err := ioutil.ReadFile("reseed_private.pem")
if nil != err {
log.Fatalln(err)
return
}
privDer, _ := pem.Decode(privPem)
privKey, err := x509.ParsePKCS1PrivateKey(privDer.Bytes)
if nil != err {
log.Fatalln(err)
return
}
// create an SU3 from the seed
su3File, err := reseeder.CreateSu3(seeds)
su3File.SetSignerId("matt@drollette.com")
// sign the su3 with our key
su3File.Sign(privKey, su3.SIGTYPE_RSA_SHA512)
//write the file to disk
ioutil.WriteFile("i2pseeds.su3", su3File.Bytes(), 0777)
}

View File

@@ -30,11 +30,11 @@ func su3VerifyAction(c *cli.Context) {
panic(err)
}
fmt.Println(su3File.String())
if err := su3File.VerifySignature(); nil != err {
panic(err)
}
fmt.Println(su3File.String())
fmt.Println("Verified signature.")
}

View File

@@ -14,8 +14,10 @@ func main() {
app.Usage = "I2P commands"
app.Flags = []cli.Flag{}
app.Commands = []cli.Command{
cmd.NewReseederCommand(),
cmd.NewReseedCommand(),
cmd.NewSu3VerifyCommand(),
cmd.NewKeygenCommand(),
cmd.NewSu3Command(),
}
if err := app.Run(os.Args); err != nil {

View File

@@ -1 +1,128 @@
package reseed
import (
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"path/filepath"
"regexp"
"sync"
"github.com/MDrollette/go-i2p/su3"
)
type routerInfo struct {
Name string
Data []byte
}
type Peer string
type Seed []routerInfo
type Reseeder interface {
// seed a peer with routerinfos
Seed(p Peer) (Seed, error)
// get a peer from a given request
Peer(r *http.Request) Peer
// create an Su3 file from the given seeds
CreateSu3(seeds Seed) (*su3.Su3File, error)
}
type ReseederImpl struct {
netdb NetDbProvider
peers map[string]Peer
m sync.Mutex
}
func NewReseeder(netdb NetDbProvider) *ReseederImpl {
return &ReseederImpl{
netdb: netdb,
peers: make(map[string]Peer),
}
}
func (rs *ReseederImpl) Seed(p Peer) (Seed, error) {
all, err := rs.netdb.RouterInfos()
if nil != err {
return nil, err
}
return Seed(all), nil
}
func (rs *ReseederImpl) Peer(r *http.Request) Peer {
rs.m.Lock()
defer rs.m.Unlock()
if p, ok := rs.peers[r.RemoteAddr]; !ok {
rs.peers[r.RemoteAddr] = Peer(r.RemoteAddr)
} else {
return p
}
return rs.peers[r.RemoteAddr]
}
func (rs *ReseederImpl) CreateSu3(seeds Seed) (*su3.Su3File, error) {
su3File := su3.NewSu3File()
su3File.FileType = su3.FILE_TYPE_ZIP
su3File.ContentType = su3.CONTENT_TYPE_RESEED
zipped, err := zipSeeds(seeds)
if nil != err {
return nil, err
}
su3File.SetContent(zipped)
return su3File, nil
}
type NetDbProvider interface {
// Get all router infos
RouterInfos() ([]routerInfo, error)
}
type LocalNetDbImpl struct {
Path string
}
func NewLocalNetDb(path string) *LocalNetDbImpl {
return &LocalNetDbImpl{
Path: path,
}
}
func (db *LocalNetDbImpl) RouterInfos() (routerInfos []routerInfo, err error) {
var src []os.FileInfo
if src, err = ioutil.ReadDir(db.Path); nil != err {
return
}
// randomize the file order
files := make([]os.FileInfo, len(src))
perm := rand.Perm(len(src))
for i, v := range perm {
files[v] = src[i]
}
r, _ := regexp.Compile("^routerInfo-[A-Za-z0-9-=~]+.dat$")
for _, file := range files {
if r.MatchString(file.Name()) {
riBytes, err := ioutil.ReadFile(filepath.Join(db.Path, file.Name()))
if nil != err {
log.Println(err)
continue
}
routerInfos = append(routerInfos, routerInfo{
Name: file.Name(),
Data: riBytes,
})
}
}
return
}

4
reseed/service.go Normal file
View File

@@ -0,0 +1,4 @@
package reseed
type Su3Provider interface {
}

32
reseed/utils.go Normal file
View File

@@ -0,0 +1,32 @@
package reseed
import (
"archive/zip"
"bytes"
)
func zipSeeds(seeds Seed) ([]byte, error) {
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new zip archive.
zipWriter := zip.NewWriter(buf)
// Add some files to the archive.
for _, file := range seeds {
zipFile, err := zipWriter.Create(file.Name)
if err != nil {
return nil, err
}
_, err = zipFile.Write(file.Data)
if err != nil {
return nil, err
}
}
if err := zipWriter.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@@ -19,6 +19,38 @@ func certForSigner(signer string) (*x509.Certificate, error) {
var (
reseedKeys = map[string][]byte{
"matt@drollette.com": []byte(`-----BEGIN CERTIFICATE-----
MIIFgjCCA2ygAwIBAgICBNIwCwYJKoZIhvcNAQELMFUxCzAJBgNVBAYTAlhYMR4w
HAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDELMAkG
A1UEBxMCWFgxCzAJBgNVBAkTAlhYMB4XDTE0MTIxMDA1MzQ1M1oXDTI0MTIxMDA1
MzQ1M1owVTELMAkGA1UEBhMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0
d29yazEMMAoGA1UECxMDSTJQMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDo0Y1eOyG/5GHJLZssrKCdtLCU
8MIfguIv0sU+Q69kGS5yCe1TlrhpSEvHDneZRWc+rbc+eKpPXUdReIFLeuS8cPh7
dkLArx88qEaff2cx1Ss4g+wErVZcPlMf+6og50Z2JxEJe/7WW1B38eQ1f+6i5j33
twruk0Xa9vAnyP+bO53PNyJw548N7qFA/nGeW/r88iYquFtGxGyVv8zMzGeHsmXm
7G+B6LG2FHecg5ZT2shaOCY27i6Rq08qiGV7+qZ6tGGUFFEr6cpcudrzA0yGzsyy
pIUdhWw2+r9AftF0Si4+ic5aIiZzmaBvuzdn6GQkmEQDt5KG6pj7RJN73qCWz85T
tUf/5QI4/0itTQnEtHFJA2Hh1OWRha6HSbHVcHuZdJUtCSKRyXHwMZgYM5e3PAW2
uEuTPA+F81AKBnDWy2FVs/I80epr8526mkRIMTswqLJ8+/MZenmzJ2fUHItwNb4e
qSMrczmlTbB4xEWGKqEb/gij8qr+6Sd4EqR6B5jDo23I39iy33QN924KirlXb2Hl
kMWGjXf/qOEdoqvCMynhathAnVtYhJ1M7scw3Z5v035j/3uidyYX+lKQpf8kSCDo
igImjYewmzCZBgU8n3iCIfhuR+Fw8l3d6f6UdEIVF9Qi0EpJ9kCXyrXYhg3ulckU
S+MQcHqjUewDJUwMLwIDAQABo2AwXjAOBgNVHQ8BAf8EBAMCAIQwHQYDVR0lBBYw
FAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wDAYDVR0OBAUE
AwECAzAOBgNVHSMEBzAFgAMBAgMwCwYJKoZIhvcNAQELA4ICAQARD/2NHcnXUE1F
W5exDU4RHQt4Y8FfwXY/ijBtdbHz39BEQ6+P5eS/JwcdXYEn3Kfoan5j42jiYZQq
LpQEJDSDYEy22FkN+NnDawdXX/PMOUs84TIYpPv5h0mESjA+3j6k+cXmnacATsIv
qv/ssrRkAL3yJ5T3MOqRTkYKWXPUAttylLbahRlMyi2l4tq1dsIuGGdEdarpkt78
r0OkYaOO3yJDgloZhDZ1TrUgZ60HKdyUSUNn3QlXU5LlMPNJ2godqRpfsgBa3XZK
fxh0kM1KPJuVy9UzGiZuKWXEGY/q/hMDKcviKFWZb2P6mIU4Q7aT9ph/kQWJG8We
GK1uqLdHqKKHRMAK+KHVVwkbwy60mMDunsl6y9Q9q9uh5Mrre7uc36uO2h1IJUNV
O+1ifGNKrd27sgTmw7RofKetM/k9x/22wU7UDnUhnBCkZOjLyYFCNF6rT0l9CvVC
1aud5NWOPIIEoYMw7QRPWijw6wNqQ1hslm7d+boz/p0b+qk/bq12sw1Nfi5wOGV7
dOZRnaoG2eCD+/RsrhbXymFbIpU+sLGuoFH4lTcfChhXvq2YgedzhrYNC6Sy1sq9
rHSwJ5eBGMqE59+Sda/JkPiMw06VtPYqiGmqLPlFxnziX0o9Gw+v4dNZvuU3DZ+y
5wYKnjW5pFMnWeBlDXkVUPOIM+/paw==
-----END CERTIFICATE-----`),
"backup@mail.i2p": []byte(`-----BEGIN CERTIFICATE-----
MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt

View File

@@ -3,14 +3,20 @@ package su3
import (
"archive/zip"
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/binary"
"fmt"
"os"
"strconv"
"time"
)
const (
MAGIC_BYTES = "I2Psu3"
MAGIC_BYTES = "I2Psu3"
MIN_VERSION_LENGTH = 16
SIGTYPE_DSA = uint16(0)
SIGTYPE_ECDSA_SHA256 = uint16(1)
@@ -34,14 +40,14 @@ const (
type Su3File struct {
Magic [6]byte
Format [1]byte
Format uint8
SignatureType uint16
SignatureLength uint16
VersionLength uint8
SignerIdLength uint8
ContentLength uint64
FileType [1]byte
ContentType [1]byte
FileType uint8
ContentType uint16
Version []byte
SignerId []byte
@@ -50,6 +56,129 @@ type Su3File struct {
SignedBytes []byte
}
func NewSu3File() *Su3File {
var a [6]byte
copy(a[:], MAGIC_BYTES)
s := Su3File{Magic: a}
s.SetVersion(strconv.FormatInt(time.Now().Unix(), 10))
return &s
}
func (s *Su3File) SetSignerId(signer string) {
s.SignerId = []byte(signer)
s.SignerIdLength = uint8(len(s.SignerId))
}
func (s *Su3File) SetContent(content []byte) {
s.Content = content
s.ContentLength = uint64(len(s.Content))
}
func (s *Su3File) SetVersion(version string) {
s.Version = []byte(version)
minBytes := make([]byte, MIN_VERSION_LENGTH)
if len(s.Version) < len(minBytes) {
copy(minBytes, s.Version)
s.Version = minBytes
}
s.VersionLength = uint8(len(s.Version))
}
func (s *Su3File) Sign(privkey *rsa.PrivateKey, sigType uint16) error {
var hashType crypto.Hash
switch sigType {
// case SIGTYPE_DSA:
// case SIGTYPE_ECDSA_SHA256:
// case SIGTYPE_ECDSA_SHA384:
// case SIGTYPE_ECDSA_SHA512:
// case SIGTYPE_RSA_SHA256:
// case SIGTYPE_RSA_SHA384:
case SIGTYPE_RSA_SHA512:
s.SignatureType = SIGTYPE_RSA_SHA512
s.SignatureLength = uint16(512)
hashType = crypto.SHA512
default:
return fmt.Errorf("Unknown signature type")
}
h := hashType.New()
h.Write(s.ContentBytes())
digest := h.Sum(nil)
sig, err := rsa.SignPKCS1v15(rand.Reader, privkey, 0, digest)
if nil != err {
return err
}
s.Signature = sig
return nil
}
func (s *Su3File) ContentBytes() []byte {
buf := new(bytes.Buffer)
var (
skip [1]byte
bigSkip [12]byte
)
// 0-5
binary.Write(buf, binary.BigEndian, s.Magic)
// 6
binary.Write(buf, binary.BigEndian, skip)
// 7
binary.Write(buf, binary.BigEndian, s.Format)
// 8-9
binary.Write(buf, binary.BigEndian, s.SignatureType)
// 10-11
binary.Write(buf, binary.BigEndian, s.SignatureLength)
// 12
binary.Write(buf, binary.BigEndian, skip)
// 13
binary.Write(buf, binary.BigEndian, s.VersionLength)
// 14
binary.Write(buf, binary.BigEndian, skip)
// 15
binary.Write(buf, binary.BigEndian, s.SignerIdLength)
// 16-23
binary.Write(buf, binary.BigEndian, s.ContentLength)
// 24
binary.Write(buf, binary.BigEndian, skip)
// 25
binary.Write(buf, binary.BigEndian, s.FileType)
// 26
binary.Write(buf, binary.BigEndian, skip)
// 27
binary.Write(buf, binary.BigEndian, s.ContentType)
// 28-39
binary.Write(buf, binary.BigEndian, bigSkip)
// 40-55+ Version, UTF-8 padded with trailing 0x00, 16 bytes minimum, length specified at byte 13. Do not append 0x00 bytes if the length is 16 or more.
binary.Write(buf, binary.BigEndian, s.Version)
// xx+ ID of signer, (e.g. "zzz@mail.i2p") UTF-8, not padded, length specified at byte 15
binary.Write(buf, binary.BigEndian, s.SignerId)
// xx+ Content, length and format specified in header
binary.Write(buf, binary.BigEndian, s.Content)
return buf.Bytes()
}
func (s *Su3File) Bytes() []byte {
buf := new(bytes.Buffer)
buf.Write(s.ContentBytes())
// xx+ Signature, length specified in header, covers everything starting at byte 0
binary.Write(buf, binary.BigEndian, s.Signature)
return buf.Bytes()
}
func (s *Su3File) VerifySignature() error {
return verifySig(s.SignatureType, s.SignerId, s.Signature, s.SignedBytes)
}
func (s *Su3File) String() string {
var b bytes.Buffer
@@ -72,17 +201,13 @@ func (s *Su3File) String() string {
fmt.Fprintf(&b, "Version: %q\n", bytes.Trim(s.Version, "\x00"))
fmt.Fprintf(&b, "SignerId: %q\n", s.SignerId)
// fmt.Fprintf(&b, "Content: %q\n", s.Content)
// fmt.Fprintf(&b, "Signature: %q\n", s.Signature)
fmt.Fprintf(&b, "Signature: %q\n", s.Signature)
fmt.Fprintln(&b, "---------------------------")
return b.String()
}
func (s *Su3File) VerifySignature() error {
return verifySig(s.SignatureType, s.SignerId, s.Signature, s.SignedBytes)
}
func uzipData(c []byte) ([]byte, error) {
input := bytes.NewReader(c)
zipReader, err := zip.NewReader(input, int64(len(c)))