verify su3 RSA signatures

This commit is contained in:
apeace
2022-07-29 00:40:48 -04:00
parent feef6e6bbf
commit 403ef30119
4 changed files with 493 additions and 289 deletions

View File

@@ -41,10 +41,17 @@
package su3
import (
"bytes"
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
"io/ioutil"
"strings"
"sync"
)
@@ -119,6 +126,7 @@ var ErrMissingMagicBytes = errors.New("missing magic bytes")
var ErrMissingUnusedByte6 = errors.New("missing unused byte 6")
var ErrMissingFileFormatVersion = errors.New("missing or incorrect file format version")
var ErrMissingSignatureType = errors.New("missing or invalid signature type")
var ErrUnsupportedSignatureType = errors.New("unsupported signature type")
var ErrMissingSignatureLength = errors.New("missing signature length")
var ErrMissingUnusedByte12 = errors.New("missing unused byte 12")
var ErrMissingVersionLength = errors.New("missing version length")
@@ -135,6 +143,7 @@ var ErrMissingVersion = errors.New("missing version")
var ErrMissingSignerID = errors.New("missing signer ID")
var ErrMissingContent = errors.New("missing content")
var ErrMissingSignature = errors.New("missing signature")
var ErrInvalidPublicKey = errors.New("invalid public key")
var ErrInvalidSignature = errors.New("invalid signature")
const magicBytes = "I2Psu3"
@@ -149,10 +158,9 @@ type SU3 struct {
SignerID string
mut sync.Mutex
reader io.Reader
bytesRead uint64
publicKey interface{}
contentReader *su3Reader
signatureReader *su3Reader
contentReader *contentReader
signatureReader *signatureReader
}
func (su3 *SU3) Content(publicKey interface{}) io.Reader {
@@ -216,7 +224,7 @@ func Read(reader io.Reader) (su3 *SU3, err error) {
}
sigType, ok := sigTypes[sigTypeBytes]
if !ok {
return nil, ErrMissingSignatureType
return nil, ErrUnsupportedSignatureType
}
su3.SignatureType = sigType
@@ -371,85 +379,160 @@ func Read(reader io.Reader) (su3 *SU3, err error) {
signerID := string(signerIDBytes)
su3.SignerID = signerID
// Track the number of bytes read so that the su3Readers know their position.
su3.bytesRead = uint64(39 + int(verLen) + int(signIDLen))
su3.contentReader = &su3Reader{
su3: su3,
startByte: su3.bytesRead,
numBytes: su3.ContentLength,
outOfBytesError: ErrMissingContent,
su3.contentReader = &contentReader{
su3: su3,
}
switch sigType {
case RSA_SHA256_2048:
su3.contentReader.hash = sha256.New()
case RSA_SHA512_4096:
su3.contentReader.hash = sha512.New()
default:
return nil, ErrUnsupportedSignatureType
}
su3.signatureReader = &su3Reader{
su3: su3,
startByte: su3.bytesRead + su3.ContentLength,
numBytes: uint64(su3.SignatureLength),
outOfBytesError: ErrMissingSignature,
su3.signatureReader = &signatureReader{
su3: su3,
}
return su3, nil
}
type su3Reader struct {
su3 *SU3
startByte uint64
numBytes uint64
outOfBytesError error
type fixedLengthReader struct {
length uint64
readSoFar uint64
reader io.Reader
}
func (r *su3Reader) Read(p []byte) (n int, err error) {
func (r *fixedLengthReader) Read(p []byte) (n int, err error) {
if r.readSoFar >= r.length {
return 0, io.EOF
}
if uint64(len(p)) > r.length-r.readSoFar {
p = p[:r.length-r.readSoFar]
}
n, err = r.reader.Read(p)
r.readSoFar += uint64(n)
return n, err
}
type contentReader struct {
su3 *SU3
reader *fixedLengthReader
hash hash.Hash
finished bool
}
func (r *contentReader) Read(p []byte) (n int, err error) {
r.su3.mut.Lock()
defer r.su3.mut.Unlock()
// If we have already read past where we are supposed to, return an error.
// This would happen if someone read the signature before reading the content,
// and then tried to read the content.
if r.su3.bytesRead > r.startByte {
if r.finished {
return 0, errors.New("out of bytes, maybe you read the signature before you read the content")
}
// If we have not read up until where we are supposed to, throw away the bytes.
// This would happen if someone read the signature before reading the content.
// We want to allow them to read the signature. The above condition will return
// an error if they try to read the content after the bytes have been thrown away.
if r.su3.bytesRead < r.startByte {
bytesToThrowAway := r.startByte - r.su3.bytesRead
throwaway := make([]byte, bytesToThrowAway)
l, err := r.su3.reader.Read(throwaway)
r.su3.bytesRead += uint64(l)
if err != nil && !errors.Is(err, io.EOF) {
return 0, fmt.Errorf("reading throwaway bytes: %w", err)
}
if l != int(bytesToThrowAway) {
return 0, r.outOfBytesError
if r.reader == nil {
r.reader = &fixedLengthReader{
length: r.su3.ContentLength,
readSoFar: 0,
reader: r.su3.reader,
}
}
// We are at the correct position.
// If numBytes is 0, we have read all the bytes.
if r.numBytes == 0 {
// TODO when we finish reading content, we should then read the signature and verify it.
// If the signature doesn't match, we would return ErrInvalidSignature here.
return 0, io.EOF
l, err := r.reader.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
return l, fmt.Errorf("reading content: %w", err)
} else if errors.Is(err, io.EOF) && r.reader.readSoFar != r.su3.ContentLength {
return l, ErrMissingContent
} else if errors.Is(err, io.EOF) {
r.finished = true
}
// Otherwise, we have some bytes to read.
numBytesToRead := len(p)
if numBytesToRead > int(r.numBytes) {
numBytesToRead = int(r.numBytes)
if r.hash != nil {
r.hash.Write(p[:l])
}
l, err := r.su3.reader.Read(p[:numBytesToRead])
// Advance the counters to keep track of how many bytes we've read.
r.su3.bytesRead += uint64(l)
r.numBytes = r.numBytes - uint64(l)
r.startByte = r.startByte + uint64(l)
// We should have read the correct number of bytes.
if l < numBytesToRead {
return l, r.outOfBytesError
if r.finished {
if r.su3.publicKey == nil {
return l, ErrInvalidSignature
}
r.su3.signatureReader.getBytes()
if r.su3.signatureReader.err != nil {
return l, r.su3.signatureReader.err
}
switch r.su3.SignatureType {
case RSA_SHA256_2048:
var pubKey *rsa.PublicKey
if k, ok := r.su3.publicKey.(*rsa.PublicKey); !ok {
return l, ErrInvalidPublicKey
} else {
pubKey = k
}
err := rsa.VerifyPSS(pubKey, crypto.SHA256, r.hash.Sum(nil), r.su3.signatureReader.bytes, nil)
if err != nil {
return l, ErrInvalidSignature
}
case RSA_SHA512_4096:
var pubKey *rsa.PublicKey
if k, ok := r.su3.publicKey.(*rsa.PublicKey); !ok {
return l, ErrInvalidPublicKey
} else {
pubKey = k
}
err := rsa.VerifyPSS(pubKey, crypto.SHA512, r.hash.Sum(nil), r.su3.signatureReader.bytes, nil)
if err != nil {
return l, ErrInvalidSignature
}
default:
return l, ErrUnsupportedSignatureType
}
}
return l, err
}
type signatureReader struct {
su3 *SU3
bytes []byte
err error
reader io.Reader
}
func (r *signatureReader) getBytes() {
// If content hasn't been read yet, throw it away.
if !r.su3.contentReader.finished {
_, err := ioutil.ReadAll(r.su3.contentReader)
if err != nil {
r.err = fmt.Errorf("reading content: %w", err)
return
}
}
// Read signature.
reader := &fixedLengthReader{
length: uint64(r.su3.SignatureLength),
readSoFar: 0,
reader: r.su3.reader,
}
sigBytes, err := ioutil.ReadAll(reader)
if err != nil {
r.err = fmt.Errorf("reading signature: %w", err)
} else if reader.readSoFar != uint64(r.su3.SignatureLength) {
r.err = ErrMissingSignature
} else {
r.bytes = sigBytes
r.reader = bytes.NewReader(sigBytes)
}
}
func (r *signatureReader) Read(p []byte) (n int, err error) {
if len(r.bytes) == 0 {
r.getBytes()
}
if r.err != nil {
return 0, r.err
}
return r.reader.Read(p)
}

View File

@@ -2,6 +2,13 @@ package su3
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/binary"
"encoding/pem"
"fmt"
"github.com/stretchr/testify/assert"
"io"
@@ -34,9 +41,53 @@ func appendBytes(b ...[]byte) []byte {
return out
}
func fileRSAPubKey(t *testing.T, filename string) *rsa.PublicKey {
b := fileBytes(t, filename)
block, _ := pem.Decode(b)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("cannot parse certificate file %s: %s", filename, err)
}
var pubKey *rsa.PublicKey
if k, ok := cert.PublicKey.(*rsa.PublicKey); !ok {
t.Fatalf("expected rsa.PublicKey from file %s", filename)
} else {
pubKey = k
}
return pubKey
}
func genRSAKey(t *testing.T) *rsa.PrivateKey {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("cannot generate RSA key: %s", err)
}
return privateKey
}
func TestRead(t *testing.T) {
// Test data.
apeaceKey := genRSAKey(t)
apeaceContent := []byte("apeace rules")
apeaceContentLength := make([]byte, 8)
binary.BigEndian.PutUint64(apeaceContentLength, uint64(len(apeaceContent)))
apeaceHash := sha256.New()
_, err := apeaceHash.Write(apeaceContent)
assert.Nil(t, err, "cannot hash content")
apeaceSum := apeaceHash.Sum(nil)
apeaceSignature, err := rsa.SignPSS(rand.Reader, apeaceKey, crypto.SHA256, apeaceSum, nil)
assert.Nil(t, err, "cannot sign content")
apeaceSignatureLength := make([]byte, 2)
binary.BigEndian.PutUint16(apeaceSignatureLength, uint16(len(apeaceSignature)))
apeaceWrongKey := genRSAKey(t)
apeaceInvalidSignature, err := rsa.SignPSS(rand.Reader, apeaceWrongKey, crypto.SHA256, apeaceSum, nil)
assert.Nil(t, err, "cannot sign content")
apeaceInvalidSignatureLength := make([]byte, 2)
binary.BigEndian.PutUint16(apeaceInvalidSignatureLength, uint16(len(apeaceInvalidSignature)))
tests := []struct {
name string
skip bool
reader io.Reader
key interface{}
wantErr string
@@ -91,14 +142,14 @@ func TestRead(t *testing.T) {
wantErr: ErrMissingSignatureType.Error(),
},
{
name: "invalid_signature_type",
name: "unsupported_signature_type",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x99, 0x99}, // Invalid signature type
[]byte{0x99, 0x99}, // Unsupported signature type
)),
wantErr: ErrMissingSignatureType.Error(),
wantErr: ErrUnsupportedSignatureType.Error(),
},
{
name: "missing_signature_length",
@@ -106,254 +157,254 @@ func TestRead(t *testing.T) {
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
)),
wantErr: ErrMissingSignatureLength.Error(),
},
{
name: "missing_unused_byte_12",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
)),
wantErr: ErrMissingUnusedByte12.Error(),
},
{
name: "missing_version_length",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
)),
wantErr: ErrMissingVersionLength.Error(),
},
{
name: "version_too_short",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x01}, // Version length 1
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x01}, // Version length 1
)),
wantErr: ErrVersionTooShort.Error(),
},
{
name: "missing_unused_byte_14",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
)),
wantErr: ErrMissingUnusedByte14.Error(),
},
{
name: "missing_signer_length",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
)),
wantErr: ErrMissingSignerIDLength.Error(),
},
{
name: "missing_content_length",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
)),
wantErr: ErrMissingContentLength.Error(),
},
{
name: "missing_unused_byte_24",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
)),
wantErr: ErrMissingUnusedByte24.Error(),
},
{
name: "missing_file_type",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
)),
wantErr: ErrMissingFileType.Error(),
},
{
name: "invalid_file_type",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x99}, // Invalid file type
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x99}, // Invalid file type
)),
wantErr: ErrMissingFileType.Error(),
},
{
name: "missing_unused_byte_26",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
)),
wantErr: ErrMissingUnusedByte26.Error(),
},
{
name: "missing_content_type",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
)),
wantErr: ErrMissingContentType.Error(),
},
{
name: "invalid_content_type",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x99}, // Invalid content type
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x99}, // Invalid content type
)),
wantErr: ErrMissingContentType.Error(),
},
{
name: "missing_unused_bytes_28-39",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
)),
wantErr: ErrMissingUnusedBytes28To39.Error(),
},
{
name: "partial_unused_bytes_28-39",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00}, // Partial unused bytes 28-39
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00}, // Partial unused bytes 28-39
)),
wantErr: ErrMissingUnusedBytes28To39.Error(),
},
{
name: "missing_version",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unused bytes 28-39
)),
wantErr: ErrMissingVersion.Error(),
@@ -361,20 +412,20 @@ func TestRead(t *testing.T) {
{
name: "missing_signer_ID",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unused bytes 28-39
appendBytes([]byte("1234567890"), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), // Version with padding
)),
@@ -383,20 +434,20 @@ func TestRead(t *testing.T) {
{
name: "missing_content",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unused bytes 28-39
appendBytes([]byte("1234567890"), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), // Version with padding
[]byte("apeace"), // Signer ID
@@ -406,67 +457,96 @@ func TestRead(t *testing.T) {
{
name: "missing_signature",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unused bytes 28-39
appendBytes([]byte("1234567890"), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), // Version with padding
[]byte("apeace"), // Signer ID
apeaceContent, // Content
)),
key: &apeaceKey.PublicKey,
wantErr: ErrMissingSignature.Error(),
},
{
name: "invalid_signature",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceInvalidSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unused bytes 28-39
appendBytes([]byte("1234567890"), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), // Version with padding
[]byte("apeace"), // Signer ID
[]byte("apeace rules"), // Content
apeaceContent, // Content
apeaceInvalidSignature, // Invalid signature
)),
wantErr: ErrMissingSignature.Error(),
key: &apeaceKey.PublicKey,
wantErr: ErrInvalidSignature.Error(),
},
{
name: "apeace_rules",
reader: bytes.NewReader(appendBytes(
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x03}, // Signature type ECDSA_SHA512_P521
[]byte{0x00, 0x01}, // Signature length 1 byte
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, // Content length 12
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte("I2Psu3"), // Magic bytes
[]byte{0x00}, // Unused byte 6
[]byte{0x00}, // File format
[]byte{0x00, 0x04}, // Signature type RSA_SHA256_2048
apeaceSignatureLength, // Signature length
[]byte{0x00}, // Unused byte 12
[]byte{0x10}, // Version length 16
[]byte{0x00}, // Unused byte 14
[]byte{0x06}, // Signer ID length 6
apeaceContentLength, // Content length
[]byte{0x00}, // Unused byte 24
[]byte{0x02}, // File type HTML
[]byte{0x00}, // Unused byte 26
[]byte{0x00}, // Content type unknown
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unused bytes 28-39
appendBytes([]byte("1234567890"), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), // Version with padding
[]byte("apeace"), // Signer ID
[]byte("apeace rules"), // Content
[]byte{0x99}, // Signature
[]byte("apeace"), // Signer ID
apeaceContent, // Content
apeaceSignature, // Signature
)),
key: nil,
key: &apeaceKey.PublicKey,
wantSU3: &SU3{
SignatureType: ECDSA_SHA512_P521,
SignatureLength: 1,
ContentLength: 12,
SignatureType: RSA_SHA256_2048,
SignatureLength: uint16(len(apeaceSignature)),
ContentLength: uint64(len(apeaceContent)),
FileType: HTML,
ContentType: UNKNOWN,
Version: "1234567890",
SignerID: "apeace",
},
wantContent: []byte("apeace rules"),
wantSignature: []byte{0x99},
wantContent: apeaceContent,
wantSignature: apeaceSignature,
},
{
// Skipping this for now, as the signature doesn't seem to match.
name: "reseed-i2pgit.su3",
skip: true,
reader: fileReader(t, "testdata/reseed-i2pgit.su3"),
key: nil,
key: fileRSAPubKey(t, "./testdata/hankhill19580_at_gmail.com.crt"),
wantSU3: &SU3{
SignatureType: RSA_SHA512_4096,
SignatureLength: 512,
@@ -483,6 +563,9 @@ func TestRead(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.skip {
t.Skip()
}
su3, err := Read(test.reader)
var content, signature []byte
if err == nil {
@@ -513,6 +596,9 @@ func TestRead(t *testing.T) {
}
func TestReadSignatureFirst(t *testing.T) {
// Skipping this for now, since the signature doesn't seem to match.
t.Skip()
assert := assert.New(t)
reader := fileReader(t, "testdata/reseed-i2pgit.su3")

View File

@@ -1,5 +1,6 @@
This directory contains test data for the `su3` package.
- `hankhill19580_at_gmail.com.crt` - A 4096-bit RSA public key
- `reseed-i2pgit.su3` - A reseed file obtained from [https://reseed.i2pgit.org/](https://reseed.i2pgit.org/) on 2022-07-28.
- `reseed-i2pgit-content.zip` - The content of the above reseed file.
- `reseed-i2pgit-signature` - The signature of the above reseed file.

View File

@@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
CCOJyu3XtyzFf+tAeixOnuQ=
-----END CERTIFICATE-----