Files
go-i2p/lib/su3

su3

-- import "github.com/go-i2p/go-i2p/lib/su3"

su3.svg

Package su3 implements reading the SU3 file format.

SU3 files provide content that is signed by a known identity. They are used to distribute many types of data, including reseed files, plugins, blocklists, and more.

See: https://geti2p.net/spec/updates#su3-file-specification

The Read() function takes an io.Reader, and it returns a *SU3. The *SU3 contains the SU3 file metadata, such as the type of the content and the signer ID. In order to get the file contents, one must pass in the public key associated with the file's signer, so that the signature can be validated. The content can still be read without passing in the key, but after returning the full content the error ErrInvalidSignature will be returned.

Example usage:

    // Let's say we are reading an SU3 file from an HTTP body, which is an io.Reader.
    su3File, err := su3.Read(body)
    if err != nil {
        // Handle error.
    }
    // Look up this signer's key.
    key := somehow_lookup_the_key(su3File.SignerID)
    // Read the content.
    contentReader := su3File.Content(key)
    bytes, err := ioutil.ReadAll(contentReader)
    if errors.Is(err, su3.ErrInvalidSignature) {
	       // The signature is invalid, OR a nil key was provided.
    } else if err != nil {
        // Handle error.
    }

If you want to parse from a []byte, you can wrap it like this:

mySU3FileBytes := []byte{0x00, 0x01, 0x02, 0x03}
su3File, err := su3.Read(bytes.NewReader(mySU3FileBytes))

One of the advantages of this library's design is that you can avoid buffering the file contents in memory. Here's how you would stream from an HTTP body directly to disk:

    su3File, err := su3.Read(body)
    if err != nil {
	       // Handle error.
    }
    // Look up this signer's key.
    key := somehow_lookup_the_key(su3File.SignerID)
    // Stream directly to disk.
    f, err := os.Create("my_file.txt")
    if err != nil {
	       // Handle error.
    }
    _, err := io.Copy(f, su3File.Content(key))
    if errors.Is(err, su3.ErrInvalidSignature) {
	       // The signature is invalid, OR a nil key was provided.
        // Don't trust the file, delete it!
    } else if err != nil {
        // Handle error.
    }

Note: if you want to read the content, the Content() io.Reader must be read before the Signature() io.Reader. If you read the signature first, the content bytes will be thrown away. If you then attempt to read the content, you will get an error. For clarification, see TestReadSignatureFirst.

Usage

var (
	ErrMissingMagicBytes        = oops.Errorf("missing magic bytes")
	ErrMissingUnusedByte6       = oops.Errorf("missing unused byte 6")
	ErrMissingFileFormatVersion = oops.Errorf("missing or incorrect file format version")
	ErrMissingSignatureType     = oops.Errorf("missing or invalid signature type")
	ErrUnsupportedSignatureType = oops.Errorf("unsupported signature type")
	ErrMissingSignatureLength   = oops.Errorf("missing signature length")
	ErrMissingUnusedByte12      = oops.Errorf("missing unused byte 12")
	ErrMissingVersionLength     = oops.Errorf("missing version length")
	ErrVersionTooShort          = oops.Errorf("version length too short")
	ErrMissingUnusedByte14      = oops.Errorf("missing unused byte 14")
	ErrMissingSignerIDLength    = oops.Errorf("missing signer ID length")
	ErrMissingContentLength     = oops.Errorf("missing content length")
	ErrMissingUnusedByte24      = oops.Errorf("missing unused byte 24")
	ErrMissingFileType          = oops.Errorf("missing or invalid file type")
	ErrMissingUnusedByte26      = oops.Errorf("missing unused byte 26")
	ErrMissingContentType       = oops.Errorf("missing or invalid content type")
	ErrMissingUnusedBytes28To39 = oops.Errorf("missing unused bytes 28-39")
	ErrMissingVersion           = oops.Errorf("missing version")
	ErrMissingSignerID          = oops.Errorf("missing signer ID")
	ErrMissingContent           = oops.Errorf("missing content")
	ErrMissingSignature         = oops.Errorf("missing signature")
	ErrInvalidPublicKey         = oops.Errorf("invalid public key")
	ErrInvalidSignature         = oops.Errorf("invalid signature")
)

type ContentType

type ContentType string
const (
	UNKNOWN       ContentType = "unknown"
	ROUTER_UPDATE ContentType = "router_update"
	PLUGIN        ContentType = "plugin"
	RESEED        ContentType = "reseed"
	NEWS          ContentType = "news"
	BLOCKLIST     ContentType = "blocklist"
)

type FileType

type FileType string
const (
	ZIP      FileType = "zip"
	XML      FileType = "xml"
	HTML     FileType = "html"
	XML_GZIP FileType = "xml.gz"
	TXT_GZIP FileType = "txt.gz"
	DMG      FileType = "dmg"
	EXE      FileType = "exe"
)

type SU3

type SU3 struct {
	SignatureType   SignatureType
	SignatureLength uint16
	ContentLength   uint64
	FileType        FileType
	ContentType     ContentType
	Version         string
	SignerID        string
}

func Read

func Read(reader io.Reader) (su3 *SU3, err error)

func (*SU3) Content

func (su3 *SU3) Content(publicKey interface{}) io.Reader

func (*SU3) Signature

func (su3 *SU3) Signature() io.Reader

type SignatureType

type SignatureType string
const (
	DSA_SHA1               SignatureType = "DSA-SHA1"
	ECDSA_SHA256_P256      SignatureType = "ECDSA-SHA256-P256"
	ECDSA_SHA384_P384      SignatureType = "ECDSA-SHA384-P384"
	ECDSA_SHA512_P521      SignatureType = "ECDSA-SHA512-P521"
	RSA_SHA256_2048        SignatureType = "RSA-SHA256-2048"
	RSA_SHA384_3072        SignatureType = "RSA-SHA384-3072"
	RSA_SHA512_4096        SignatureType = "RSA-SHA512-4096"
	EdDSA_SHA512_Ed25519ph SignatureType = "EdDSA-SHA512-Ed25519ph"
)

su3

github.com/go-i2p/go-i2p/lib/su3

go-i2p template file