88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package i2pkeys
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// HashSize is the size of an I2P destination hash in bytes
|
|
HashSize = 32
|
|
|
|
// B32AddressLength is the length of a base32 address without suffix
|
|
B32AddressLength = 52
|
|
|
|
// FullB32Length is the total length of a .b32.i2p address
|
|
FullB32Length = 60
|
|
|
|
// B32Padding is the padding used for base32 encoding
|
|
B32Padding = "===="
|
|
|
|
// B32Suffix is the standard suffix for base32 I2P addresses
|
|
B32Suffix = ".b32.i2p"
|
|
)
|
|
|
|
// I2PDestHash represents a 32-byte I2P destination hash.
|
|
// It's commonly represented as a base32-encoded address with a .b32.i2p suffix.
|
|
type I2PDestHash [HashSize]byte
|
|
|
|
// DestHashFromString creates a destination hash from a base32-encoded string.
|
|
// The input should be in the format "base32address.b32.i2p".
|
|
func DestHashFromString(addr string) (I2PDestHash, error) {
|
|
if !isValidB32Address(addr) {
|
|
return I2PDestHash{}, fmt.Errorf("invalid address format: %s", addr)
|
|
}
|
|
|
|
var hash I2PDestHash
|
|
b32Input := addr[:B32AddressLength] + B32Padding
|
|
|
|
n, err := i2pB32enc.Decode(hash[:], []byte(b32Input))
|
|
if err != nil {
|
|
return I2PDestHash{}, fmt.Errorf("decoding base32 address: %w", err)
|
|
}
|
|
|
|
if n != HashSize {
|
|
return I2PDestHash{}, fmt.Errorf("decoded hash has invalid length: got %d, want %d", n, HashSize)
|
|
}
|
|
|
|
return hash, nil
|
|
}
|
|
|
|
// isValidB32Address checks if the address has the correct format and length
|
|
func isValidB32Address(addr string) bool {
|
|
return strings.HasSuffix(addr, B32Suffix) && len(addr) == FullB32Length
|
|
}
|
|
|
|
// DestHashFromBytes creates a destination hash from a byte slice.
|
|
// The input must be exactly 32 bytes long.
|
|
func DestHashFromBytes(data []byte) (I2PDestHash, error) {
|
|
if len(data) != HashSize {
|
|
return I2PDestHash{}, fmt.Errorf("invalid hash length: got %d, want %d", len(data), HashSize)
|
|
}
|
|
|
|
var hash I2PDestHash
|
|
copy(hash[:], data)
|
|
return hash, nil
|
|
}
|
|
|
|
// String returns the base32-encoded representation with the .b32.i2p suffix.
|
|
func (h I2PDestHash) String() string {
|
|
encoded := make([]byte, i2pB32enc.EncodedLen(HashSize))
|
|
i2pB32enc.Encode(encoded, h[:])
|
|
return string(encoded[:B32AddressLength]) + B32Suffix
|
|
}
|
|
|
|
// Hash returns the base64-encoded SHA-256 hash of the destination hash.
|
|
func (h I2PDestHash) Hash() string {
|
|
digest := sha256.Sum256(h[:])
|
|
encoded := make([]byte, i2pB64enc.EncodedLen(len(digest)))
|
|
i2pB64enc.Encode(encoded, digest[:])
|
|
return string(encoded[:44])
|
|
}
|
|
|
|
// Network returns the network type, always "I2P".
|
|
func (h I2PDestHash) Network() string {
|
|
return "I2P"
|
|
}
|