mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-01-13 06:07:14 +08:00
Compare commits
18 Commits
copilot/su
...
dns-rcode2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
964ebe8ffa | ||
|
|
b69a376aa1 | ||
|
|
12f4a014e0 | ||
|
|
9cc7907234 | ||
|
|
21a9658519 | ||
|
|
7f436f5318 | ||
|
|
dcfde8dc92 | ||
|
|
898db92d51 | ||
|
|
8dd0e388a2 | ||
|
|
40f0a541bf | ||
|
|
1762d6c8cc | ||
|
|
195248801d | ||
|
|
4a825c0260 | ||
|
|
514c9e5a22 | ||
|
|
2f366aed2e | ||
|
|
c0c88f3d73 | ||
|
|
d0344bcff8 | ||
|
|
a6ebb3061c |
@@ -45,6 +45,7 @@
|
||||
- [wulabing/xray_docker](https://github.com/wulabing/xray_docker)
|
||||
- Web Panel - **WARNING: Please DO NOT USE plain HTTP panels like 3X-UI**, as they are believed to be bribed by Iran GFW for supporting plain HTTP by default and refused to change (https://github.com/XTLS/Xray-core/pull/3884#issuecomment-2439595331), which has already put many users' data security in danger in the past few years. **If you are already using 3X-UI, please switch to the following panels, which are verified to support HTTPS and SSH port forwarding only:**
|
||||
- [X-Panel](https://github.com/xeefei/X-Panel)
|
||||
- [PasarGuard](https://github.com/PasarGuard/panel)
|
||||
- [Remnawave](https://github.com/remnawave/panel)
|
||||
- [Marzban](https://github.com/Gozargah/Marzban)
|
||||
- [Xray-UI](https://github.com/qist/xray-ui)
|
||||
|
||||
@@ -108,7 +108,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||
}
|
||||
h.proxyConfig = proxyConfig
|
||||
|
||||
ctx = session.ContextWithHandler(ctx, h)
|
||||
ctx = session.ContextWithFullHandler(ctx, h)
|
||||
|
||||
rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
|
||||
if err != nil {
|
||||
|
||||
@@ -198,9 +198,11 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
|
||||
|
||||
func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
|
||||
if !isInternalDomain(dest) {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.Tag,
|
||||
})
|
||||
if session.InboundFromContext(ctx) == nil {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.Tag,
|
||||
})
|
||||
}
|
||||
return w.Dispatcher.Dispatch(ctx, dest)
|
||||
}
|
||||
|
||||
@@ -221,9 +223,11 @@ func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*tra
|
||||
|
||||
func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, link *transport.Link) error {
|
||||
if !isInternalDomain(dest) {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.Tag,
|
||||
})
|
||||
if session.InboundFromContext(ctx) == nil {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.Tag,
|
||||
})
|
||||
}
|
||||
return w.Dispatcher.DispatchLink(ctx, dest, link)
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +264,11 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||
transferType = protocol.TransferTypePacket
|
||||
}
|
||||
s.transferType = transferType
|
||||
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx))
|
||||
var inbound *session.Inbound
|
||||
if session.IsReverseMuxFromContext(ctx) {
|
||||
inbound = session.InboundFromContext(ctx)
|
||||
}
|
||||
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx), inbound)
|
||||
defer s.Close(false)
|
||||
defer writer.Close()
|
||||
|
||||
@@ -384,7 +388,7 @@ func (m *ClientWorker) fetchOutput() {
|
||||
|
||||
var meta FrameMetadata
|
||||
for {
|
||||
err := meta.Unmarshal(reader)
|
||||
err := meta.Unmarshal(reader, false)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != io.EOF {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to read metadata")
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
)
|
||||
|
||||
type SessionStatus byte
|
||||
@@ -60,6 +61,7 @@ type FrameMetadata struct {
|
||||
Option bitmask.Byte
|
||||
SessionStatus SessionStatus
|
||||
GlobalID [8]byte
|
||||
Inbound *session.Inbound
|
||||
}
|
||||
|
||||
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
@@ -79,11 +81,23 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
case net.Network_UDP:
|
||||
common.Must(b.WriteByte(byte(TargetNetworkUDP)))
|
||||
}
|
||||
|
||||
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.UDP != nil { // make sure it's user's proxy request
|
||||
if f.Inbound != nil {
|
||||
if f.Inbound.Source.Network == net.Network_TCP || f.Inbound.Source.Network == net.Network_UDP {
|
||||
common.Must(b.WriteByte(byte(f.Inbound.Source.Network - 1)))
|
||||
if err := addrParser.WriteAddressPort(b, f.Inbound.Source.Address, f.Inbound.Source.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if f.Inbound.Local.Network == net.Network_TCP || f.Inbound.Local.Network == net.Network_UDP {
|
||||
common.Must(b.WriteByte(byte(f.Inbound.Local.Network - 1)))
|
||||
if err := addrParser.WriteAddressPort(b, f.Inbound.Local.Address, f.Inbound.Local.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if b.UDP != nil { // make sure it's user's proxy request
|
||||
b.Write(f.GlobalID[:]) // no need to check whether it's empty
|
||||
}
|
||||
} else if b.UDP != nil {
|
||||
@@ -97,7 +111,7 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
}
|
||||
|
||||
// Unmarshal reads FrameMetadata from the given reader.
|
||||
func (f *FrameMetadata) Unmarshal(reader io.Reader) error {
|
||||
func (f *FrameMetadata) Unmarshal(reader io.Reader, readSourceAndLocal bool) error {
|
||||
metaLen, err := serial.ReadUint16(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -112,12 +126,12 @@ func (f *FrameMetadata) Unmarshal(reader io.Reader) error {
|
||||
if _, err := b.ReadFullFrom(reader, int32(metaLen)); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.UnmarshalFromBuffer(b)
|
||||
return f.UnmarshalFromBuffer(b, readSourceAndLocal)
|
||||
}
|
||||
|
||||
// UnmarshalFromBuffer reads a FrameMetadata from the given buffer.
|
||||
// Visible for testing only.
|
||||
func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer, readSourceAndLocal bool) error {
|
||||
if b.Len() < 4 {
|
||||
return errors.New("insufficient buffer: ", b.Len())
|
||||
}
|
||||
@@ -150,6 +164,54 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
}
|
||||
}
|
||||
|
||||
if f.SessionStatus == SessionStatusNew && readSourceAndLocal {
|
||||
f.Inbound = &session.Inbound{}
|
||||
|
||||
if b.Len() == 0 {
|
||||
return nil // for heartbeat, etc.
|
||||
}
|
||||
network := TargetNetwork(b.Byte(0))
|
||||
if network == 0 {
|
||||
return nil // may be padding
|
||||
}
|
||||
b.Advance(1)
|
||||
addr, port, err := addrParser.ReadAddressPort(nil, b)
|
||||
if err != nil {
|
||||
return errors.New("reading source: failed to parse address and port").Base(err)
|
||||
}
|
||||
switch network {
|
||||
case TargetNetworkTCP:
|
||||
f.Inbound.Source = net.TCPDestination(addr, port)
|
||||
case TargetNetworkUDP:
|
||||
f.Inbound.Source = net.UDPDestination(addr, port)
|
||||
default:
|
||||
return errors.New("reading source: unknown network type: ", network)
|
||||
}
|
||||
|
||||
if b.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
network = TargetNetwork(b.Byte(0))
|
||||
if network == 0 {
|
||||
return nil
|
||||
}
|
||||
b.Advance(1)
|
||||
addr, port, err = addrParser.ReadAddressPort(nil, b)
|
||||
if err != nil {
|
||||
return errors.New("reading local: failed to parse address and port").Base(err)
|
||||
}
|
||||
switch network {
|
||||
case TargetNetworkTCP:
|
||||
f.Inbound.Local = net.TCPDestination(addr, port)
|
||||
case TargetNetworkUDP:
|
||||
f.Inbound.Local = net.UDPDestination(addr, port)
|
||||
default:
|
||||
return errors.New("reading local: unknown network type: ", network)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Application data is essential, to test whether the pipe is closed.
|
||||
if f.SessionStatus == SessionStatusNew && f.Option.Has(OptionData) &&
|
||||
f.Target.Network == net.Network_UDP && b.Len() >= 8 {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
. "github.com/xtls/xray-core/common/mux"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
)
|
||||
|
||||
@@ -32,13 +33,13 @@ func TestReaderWriter(t *testing.T) {
|
||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
||||
|
||||
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
|
||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
|
||||
|
||||
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
|
||||
|
||||
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
|
||||
|
||||
writePayload := func(writer *Writer, payload ...byte) error {
|
||||
b := buf.New()
|
||||
@@ -62,7 +63,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 1,
|
||||
SessionStatus: SessionStatusNew,
|
||||
@@ -81,7 +82,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionStatus: SessionStatusNew,
|
||||
SessionID: 2,
|
||||
@@ -94,7 +95,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 1,
|
||||
SessionStatus: SessionStatusKeep,
|
||||
@@ -112,7 +113,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 3,
|
||||
SessionStatus: SessionStatusNew,
|
||||
@@ -131,7 +132,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 1,
|
||||
SessionStatus: SessionStatusEnd,
|
||||
@@ -143,7 +144,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 3,
|
||||
SessionStatus: SessionStatusEnd,
|
||||
@@ -155,7 +156,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 2,
|
||||
SessionStatus: SessionStatusKeep,
|
||||
@@ -173,7 +174,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 2,
|
||||
SessionStatus: SessionStatusEnd,
|
||||
@@ -187,7 +188,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
err := meta.Unmarshal(bytesReader)
|
||||
err := meta.Unmarshal(bytesReader, false)
|
||||
if err == nil {
|
||||
t.Error("nil error")
|
||||
}
|
||||
|
||||
@@ -166,6 +166,14 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu
|
||||
|
||||
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
ctx = session.SubContextFromMuxInbound(ctx)
|
||||
if meta.Inbound != nil && meta.Inbound.Source.IsValid() && meta.Inbound.Local.IsValid() {
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
newInbound := *inbound
|
||||
newInbound.Source = meta.Inbound.Source
|
||||
newInbound.Local = meta.Inbound.Local
|
||||
ctx = session.ContextWithInbound(ctx, &newInbound)
|
||||
}
|
||||
}
|
||||
errors.LogInfo(ctx, "received request for ", meta.Target)
|
||||
{
|
||||
msg := &log.AccessMessage{
|
||||
@@ -329,7 +337,7 @@ func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.Buffered
|
||||
|
||||
func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedReader) error {
|
||||
var meta FrameMetadata
|
||||
err := meta.Unmarshal(reader)
|
||||
err := meta.Unmarshal(reader, session.IsReverseMuxFromContext(ctx))
|
||||
if err != nil {
|
||||
return errors.New("failed to read metadata").Base(err)
|
||||
}
|
||||
@@ -340,7 +348,7 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
|
||||
case SessionStatusEnd:
|
||||
err = w.handleStatusEnd(&meta, reader)
|
||||
case SessionStatusNew:
|
||||
err = w.handleStatusNew(ctx, &meta, reader)
|
||||
err = w.handleStatusNew(session.ContextWithIsReverseMux(ctx, false), &meta, reader)
|
||||
case SessionStatusKeep:
|
||||
err = w.handleStatusKeep(&meta, reader)
|
||||
default:
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
@@ -16,9 +17,10 @@ type Writer struct {
|
||||
hasError bool
|
||||
transferType protocol.TransferType
|
||||
globalID [8]byte
|
||||
inbound *session.Inbound
|
||||
}
|
||||
|
||||
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte) *Writer {
|
||||
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte, inbound *session.Inbound) *Writer {
|
||||
return &Writer{
|
||||
id: id,
|
||||
dest: dest,
|
||||
@@ -26,6 +28,7 @@ func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType
|
||||
followup: false,
|
||||
transferType: transferType,
|
||||
globalID: globalID,
|
||||
inbound: inbound,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +46,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
|
||||
SessionID: w.id,
|
||||
Target: w.dest,
|
||||
GlobalID: w.globalID,
|
||||
Inbound: w.inbound,
|
||||
}
|
||||
|
||||
if w.followup {
|
||||
|
||||
@@ -73,7 +73,7 @@ type ResponseHeader struct {
|
||||
var (
|
||||
// Keep in sync with crypto/tls/cipher_suites.go.
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
hasGCMAsmARM64 = (cpu.ARM64.HasAES && cpu.ARM64.HasPMULL) || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm64")
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
|
||||
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ const (
|
||||
inboundSessionKey ctx.SessionKey = 1
|
||||
outboundSessionKey ctx.SessionKey = 2
|
||||
contentSessionKey ctx.SessionKey = 3
|
||||
muxPreferredSessionKey ctx.SessionKey = 4 // unused
|
||||
isReverseMuxKey ctx.SessionKey = 4 // is reverse mux
|
||||
sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark
|
||||
trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error
|
||||
dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher
|
||||
timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out
|
||||
allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp
|
||||
handlerSessionKey ctx.SessionKey = 10 // outbound gets full handler
|
||||
fullHandlerKey ctx.SessionKey = 10 // outbound gets full handler
|
||||
mitmAlpn11Key ctx.SessionKey = 11 // used by TLS dialer
|
||||
mitmServerNameKey ctx.SessionKey = 12 // used by TLS dialer
|
||||
)
|
||||
@@ -75,25 +75,21 @@ func ContentFromContext(ctx context.Context) *Content {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextWithMuxPreferred returns a new context with the given bool
|
||||
func ContextWithMuxPreferred(ctx context.Context, forced bool) context.Context {
|
||||
return context.WithValue(ctx, muxPreferredSessionKey, forced)
|
||||
func ContextWithIsReverseMux(ctx context.Context, isReverseMux bool) context.Context {
|
||||
return context.WithValue(ctx, isReverseMuxKey, isReverseMux)
|
||||
}
|
||||
|
||||
// MuxPreferredFromContext returns value in this context, or false if not contained.
|
||||
func MuxPreferredFromContext(ctx context.Context) bool {
|
||||
if val, ok := ctx.Value(muxPreferredSessionKey).(bool); ok {
|
||||
func IsReverseMuxFromContext(ctx context.Context) bool {
|
||||
if val, ok := ctx.Value(isReverseMuxKey).(bool); ok {
|
||||
return val
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContextWithSockopt returns a new context with Socket configs included
|
||||
func ContextWithSockopt(ctx context.Context, s *Sockopt) context.Context {
|
||||
return context.WithValue(ctx, sockoptSessionKey, s)
|
||||
}
|
||||
|
||||
// SockoptFromContext returns Socket configs in this context, or nil if not contained.
|
||||
func SockoptFromContext(ctx context.Context) *Sockopt {
|
||||
if sockopt, ok := ctx.Value(sockoptSessionKey).(*Sockopt); ok {
|
||||
return sockopt
|
||||
@@ -164,12 +160,12 @@ func AllowedNetworkFromContext(ctx context.Context) net.Network {
|
||||
return net.Network_Unknown
|
||||
}
|
||||
|
||||
func ContextWithHandler(ctx context.Context, handler outbound.Handler) context.Context {
|
||||
return context.WithValue(ctx, handlerSessionKey, handler)
|
||||
func ContextWithFullHandler(ctx context.Context, handler outbound.Handler) context.Context {
|
||||
return context.WithValue(ctx, fullHandlerKey, handler)
|
||||
}
|
||||
|
||||
func HandlerFromContext(ctx context.Context) outbound.Handler {
|
||||
if val, ok := ctx.Value(handlerSessionKey).(outbound.Handler); ok {
|
||||
func FullHandlerFromContext(ctx context.Context) outbound.Handler {
|
||||
if val, ok := ctx.Value(fullHandlerKey).(outbound.Handler); ok {
|
||||
return val
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
)
|
||||
@@ -33,8 +35,26 @@ func (w *PipeConnWrapper) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This Read implemented a timeout to avoid goroutine leak.
|
||||
// as a temporarily solution
|
||||
func (w *PipeConnWrapper) Read(b []byte) (n int, err error) {
|
||||
return w.R.Read(b)
|
||||
type readResult struct {
|
||||
n int
|
||||
err error
|
||||
}
|
||||
c := make(chan readResult, 1)
|
||||
go func() {
|
||||
n, err := w.R.Read(b)
|
||||
c <- readResult{n: n, err: err}
|
||||
}()
|
||||
select {
|
||||
case result := <-c:
|
||||
return result.n, result.err
|
||||
case <-time.After(300 * time.Second):
|
||||
common.Close(w.R)
|
||||
common.Interrupt(w.R)
|
||||
return 0, buf.ErrReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
|
||||
var (
|
||||
Version_x byte = 25
|
||||
Version_y byte = 9
|
||||
Version_z byte = 11
|
||||
Version_y byte = 10
|
||||
Version_z byte = 15
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -68,5 +68,6 @@ func RCodeFromError(err error) uint16 {
|
||||
if r, ok := cause.(RCodeError); ok {
|
||||
return uint16(r)
|
||||
}
|
||||
return 0
|
||||
// unknown error
|
||||
return 2
|
||||
}
|
||||
|
||||
@@ -12,14 +12,19 @@ import (
|
||||
// ResolvableContext is an implementation of routing.Context, with domain resolving capability.
|
||||
type ResolvableContext struct {
|
||||
routing.Context
|
||||
dnsClient dns.Client
|
||||
resolvedIPs []net.IP
|
||||
dnsClient dns.Client
|
||||
cacheIPs []net.IP
|
||||
hasError bool
|
||||
}
|
||||
|
||||
// GetTargetIPs overrides original routing.Context's implementation.
|
||||
func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
|
||||
if len(ctx.resolvedIPs) > 0 {
|
||||
return ctx.resolvedIPs
|
||||
if len(ctx.cacheIPs) > 0 {
|
||||
return ctx.cacheIPs
|
||||
}
|
||||
|
||||
if ctx.hasError {
|
||||
return nil
|
||||
}
|
||||
|
||||
if domain := ctx.GetTargetDomain(); len(domain) != 0 {
|
||||
@@ -29,16 +34,18 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err == nil {
|
||||
ctx.resolvedIPs = ips
|
||||
ctx.cacheIPs = ips
|
||||
return ips
|
||||
}
|
||||
errors.LogInfoInner(context.Background(), err, "resolve ip for ", domain)
|
||||
}
|
||||
|
||||
if ips := ctx.Context.GetTargetIPs(); len(ips) != 0 {
|
||||
ctx.cacheIPs = ips
|
||||
return ips
|
||||
}
|
||||
|
||||
ctx.hasError = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
25
go.mod
25
go.mod
@@ -11,23 +11,23 @@ require (
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/refraction-networking/utls v1.8.0
|
||||
github.com/quic-go/quic-go v0.55.0
|
||||
github.com/refraction-networking/utls v1.8.1
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/net v0.46.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/sys v0.37.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.75.1
|
||||
google.golang.org/protobuf v1.36.9
|
||||
google.golang.org/grpc v1.76.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.4.1
|
||||
@@ -46,13 +46,12 @@ require (
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
52
go.sum
52
go.sum
@@ -51,10 +51,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE=
|
||||
github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
||||
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
|
||||
github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo=
|
||||
github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
@@ -75,8 +75,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c h1:LHLhQY3mKXSpTcQAkjFR4/6ar3rXjQryNeM7khK3AHU=
|
||||
github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU=
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
@@ -90,22 +90,22 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
@@ -117,21 +117,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -141,12 +141,12 @@ golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uI
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -289,8 +289,8 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
|
||||
}
|
||||
if c.Xmux == (XmuxConfig{}) {
|
||||
c.Xmux.MaxConcurrency.From = 16
|
||||
c.Xmux.MaxConcurrency.To = 32
|
||||
c.Xmux.MaxConcurrency.From = 1
|
||||
c.Xmux.MaxConcurrency.To = 1
|
||||
c.Xmux.HMaxRequestTimes.From = 600
|
||||
c.Xmux.HMaxRequestTimes.To = 900
|
||||
c.Xmux.HMaxReusableSecs.From = 1800
|
||||
@@ -395,26 +395,27 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
Insecure bool `json:"allowInsecure"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
EnableSessionResumption bool `json:"enableSessionResumption"`
|
||||
DisableSystemRoot bool `json:"disableSystemRoot"`
|
||||
MinVersion string `json:"minVersion"`
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
PinnedPeerCertSha256 string `json:"pinnedPeerCertSha256"`
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
ECHServerKeys string `json:"echServerKeys"`
|
||||
ECHConfigList string `json:"echConfigList"`
|
||||
ECHForceQuery string `json:"echForceQuery"`
|
||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||
Insecure bool `json:"allowInsecure"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
EnableSessionResumption bool `json:"enableSessionResumption"`
|
||||
DisableSystemRoot bool `json:"disableSystemRoot"`
|
||||
MinVersion string `json:"minVersion"`
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"`
|
||||
PinnedPeerCertificatePublicKeySha256 *[]string `json:"pinnedPeerCertificatePublicKeySha256"`
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
ECHServerKeys string `json:"echServerKeys"`
|
||||
ECHConfigList string `json:"echConfigList"`
|
||||
ECHForceQuery string `json:"echForceQuery"`
|
||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -457,20 +458,25 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||
|
||||
if c.PinnedPeerCertSha256 != "" {
|
||||
config.PinnedPeerCertSha256 = [][]byte{}
|
||||
// Split by tilde separator
|
||||
hashes := strings.Split(c.PinnedPeerCertSha256, "~")
|
||||
for _, v := range hashes {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
hashValue, err := hex.DecodeString(v)
|
||||
if c.PinnedPeerCertificateChainSha256 != nil {
|
||||
config.PinnedPeerCertificateChainSha256 = [][]byte{}
|
||||
for _, v := range *c.PinnedPeerCertificateChainSha256 {
|
||||
hashValue, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.PinnedPeerCertSha256 = append(config.PinnedPeerCertSha256, hashValue)
|
||||
config.PinnedPeerCertificateChainSha256 = append(config.PinnedPeerCertificateChainSha256, hashValue)
|
||||
}
|
||||
}
|
||||
|
||||
if c.PinnedPeerCertificatePublicKeySha256 != nil {
|
||||
config.PinnedPeerCertificatePublicKeySha256 = [][]byte{}
|
||||
for _, v := range *c.PinnedPeerCertificatePublicKeySha256 {
|
||||
hashValue, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.PinnedPeerCertificatePublicKeySha256 = append(config.PinnedPeerCertificatePublicKeySha256, hashValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
main/commands/all/tls/certchainhash.go
Normal file
40
main/commands/all/tls/certchainhash.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
var cmdCertChainHash = &base.Command{
|
||||
UsageLine: "{{.Exec}} certChainHash",
|
||||
Short: "Calculate TLS certificates hash.",
|
||||
Long: `
|
||||
xray tls certChainHash --cert <cert.pem>
|
||||
Calculate TLS certificate chain hash.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdCertChainHash.Run = executeCertChainHash // break init loop
|
||||
}
|
||||
|
||||
var input = cmdCertChainHash.Flag.String("cert", "fullchain.pem", "The file path of the certificates chain")
|
||||
|
||||
func executeCertChainHash(cmd *base.Command, args []string) {
|
||||
fs := flag.NewFlagSet("certChainHash", flag.ContinueOnError)
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certContent, err := os.ReadFile(*input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certChainHashB64 := tls.CalculatePEMCertChainSHA256Hash(certContent)
|
||||
fmt.Println(certChainHashB64)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
var cmdLeafCertHash = &base.Command{
|
||||
UsageLine: "{{.Exec}} tls leafCertHash",
|
||||
Short: "Calculate TLS leaf certificate hash.",
|
||||
Long: `
|
||||
xray tls leafCertHash --cert <cert.pem>
|
||||
Calculate TLS leaf certificate hash.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdLeafCertHash.Run = executeLeafCertHash // break init loop
|
||||
}
|
||||
|
||||
var input = cmdLeafCertHash.Flag.String("cert", "fullchain.pem", "The file path of the leaf certificate")
|
||||
|
||||
func executeLeafCertHash(cmd *base.Command, args []string) {
|
||||
fs := flag.NewFlagSet("leafCertHash", flag.ContinueOnError)
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certContent, err := os.ReadFile(*input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certChainHashB64, err := tls.CalculatePEMLeafCertSHA256Hash(certContent)
|
||||
if err != nil {
|
||||
fmt.Println("failed to decode cert", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(certChainHashB64)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package tls
|
||||
import (
|
||||
gotls "crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
@@ -156,14 +156,8 @@ func printTLSConnDetail(tlsConn *gotls.Conn) {
|
||||
|
||||
func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
var hash []byte
|
||||
for _, asn1Data := range rawCerts {
|
||||
cert, _ := x509.ParseCertificate(asn1Data)
|
||||
if cert.IsCA {
|
||||
hash = GenerateCertHash(cert)
|
||||
}
|
||||
}
|
||||
fmt.Println("Certificate Leaf Hash: ", hex.EncodeToString(hash))
|
||||
hash := GenerateCertChainHash(rawCerts)
|
||||
fmt.Println("Certificate Chain Hash: ", base64.StdEncoding.EncodeToString(hash))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var CmdTLS = &base.Command{
|
||||
Commands: []*base.Command{
|
||||
cmdCert,
|
||||
cmdPing,
|
||||
cmdLeafCertHash,
|
||||
cmdCertChainHash,
|
||||
cmdECH,
|
||||
},
|
||||
}
|
||||
|
||||
59
main/confloader/external/external.go
vendored
59
main/confloader/external/external.go
vendored
@@ -2,6 +2,8 @@ package external
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -18,6 +20,9 @@ import (
|
||||
func ConfigLoader(arg string) (out io.Reader, err error) {
|
||||
var data []byte
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "http+unix://"):
|
||||
data, err = FetchUnixSocketHTTPContent(arg)
|
||||
|
||||
case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"):
|
||||
data, err = FetchHTTPContent(arg)
|
||||
|
||||
@@ -70,6 +75,60 @@ func FetchHTTPContent(target string) ([]byte, error) {
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// Format: http+unix:///path/to/socket.sock/api/endpoint
|
||||
func FetchUnixSocketHTTPContent(target string) ([]byte, error) {
|
||||
path := strings.TrimPrefix(target, "http+unix://")
|
||||
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return nil, errors.New("unix socket path must be absolute")
|
||||
}
|
||||
|
||||
var socketPath, httpPath string
|
||||
|
||||
sockIdx := strings.Index(path, ".sock")
|
||||
if sockIdx != -1 {
|
||||
socketPath = path[:sockIdx+5]
|
||||
httpPath = path[sockIdx+5:]
|
||||
if httpPath == "" {
|
||||
httpPath = "/"
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("cannot determine socket path, socket file should have .sock extension")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(socketPath); err != nil {
|
||||
return nil, errors.New("socket file not found: ", socketPath).Base(err)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, "unix", socketPath)
|
||||
},
|
||||
},
|
||||
}
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
resp, err := client.Get("http://localhost" + httpPath)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to fetch from unix socket: ", socketPath).Base(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode)
|
||||
}
|
||||
|
||||
content, err := buf.ReadAllToBytes(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to read response").Base(err)
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func ExtConfigLoader(files []string, reader io.Reader) (io.Reader, error) {
|
||||
buf, err := ctlcmd.Run(append([]string{"convert"}, files...), reader)
|
||||
if err != nil {
|
||||
|
||||
@@ -666,7 +666,7 @@ func (r *Reverse) Dispatch(ctx context.Context, link *transport.Link) {
|
||||
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||
}
|
||||
r.client.Dispatch(ctx, link)
|
||||
r.client.Dispatch(session.ContextWithIsReverseMux(ctx, true), link)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,11 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
||||
handler.reverse = &Reverse{
|
||||
tag: a.Reverse.Tag,
|
||||
dispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
|
||||
ctx: ctx,
|
||||
handler: handler,
|
||||
ctx: session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: a.Reverse.Tag,
|
||||
User: handler.server.User, // TODO: email
|
||||
}),
|
||||
handler: handler,
|
||||
}
|
||||
handler.reverse.monitorTask = &task.Periodic{
|
||||
Execute: handler.reverse.monitor,
|
||||
@@ -198,7 +201,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
case protocol.RequestCommandMux:
|
||||
fallthrough // let server break Mux connections that contain TCP requests
|
||||
case protocol.RequestCommandTCP:
|
||||
case protocol.RequestCommandTCP, protocol.RequestCommandRvs:
|
||||
var t reflect.Type
|
||||
var p uintptr
|
||||
if commonConn, ok := conn.(*encryption.CommonConn); ok {
|
||||
@@ -223,6 +226,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
r, _ := t.FieldByName("rawInput")
|
||||
input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
|
||||
rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
|
||||
default:
|
||||
panic("unknown VLESS request command")
|
||||
}
|
||||
default:
|
||||
ob.CanSpliceCopy = 3
|
||||
@@ -395,7 +400,7 @@ func (r *Reverse) monitor() error {
|
||||
Tag: r.tag,
|
||||
Dispatcher: r.dispatcher,
|
||||
}
|
||||
worker, err := mux.NewServerWorker(r.ctx, w, link1)
|
||||
worker, err := mux.NewServerWorker(session.ContextWithIsReverseMux(r.ctx, true), w, link1)
|
||||
if err != nil {
|
||||
errors.LogWarningInner(r.ctx, err, "failed to create mux server worker")
|
||||
return nil
|
||||
@@ -406,7 +411,7 @@ func (r *Reverse) monitor() error {
|
||||
ctx := session.ContextWithOutbounds(r.ctx, []*session.Outbound{{
|
||||
Target: net.Destination{Address: net.DomainAddress("v1.rvs.cool")},
|
||||
}})
|
||||
r.handler.Process(ctx, link2, session.HandlerFromContext(ctx).(*proxyman.Handler))
|
||||
r.handler.Process(ctx, link2, session.FullHandlerFromContext(ctx).(*proxyman.Handler))
|
||||
common.Interrupt(reader1)
|
||||
common.Interrupt(reader2)
|
||||
}()
|
||||
|
||||
@@ -92,7 +92,7 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -203,7 +203,7 @@ func TestAutoIssuingCertificate(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -304,7 +304,7 @@ func TestTLSOverKCP(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -400,7 +400,7 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -512,7 +512,7 @@ func TestGRPC(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -624,7 +624,7 @@ func TestGRPCMultiMode(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -674,7 +674,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -731,7 +731,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -743,8 +743,8 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -771,7 +771,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
certHash[1] += 1
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
@@ -829,7 +829,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -841,8 +841,8 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -869,7 +869,7 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -926,7 +926,7 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -938,9 +938,9 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -967,7 +967,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
certHash[1] += 1
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
@@ -1025,7 +1025,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -1037,9 +1037,9 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -75,8 +75,7 @@ func (c *UConn) HandshakeAddress() net.Address {
|
||||
func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
if c.Config.Show {
|
||||
localAddr := c.LocalAddr().String()
|
||||
curveID := *(*utls.CurveID)(unsafe.Pointer(reflect.ValueOf(c).Elem().FieldByName("curveID").UnsafeAddr()))
|
||||
fmt.Printf("REALITY localAddr: %v\tis using X25519MLKEM768 for TLS' communication: %v\n", localAddr, curveID == utls.X25519MLKEM768)
|
||||
fmt.Printf("REALITY localAddr: %v\tis using X25519MLKEM768 for TLS' communication: %v\n", localAddr, c.HandshakeState.ServerHello.ServerShare.Group == utls.X25519MLKEM768)
|
||||
fmt.Printf("REALITY localAddr: %v\tis using ML-DSA-65 for cert's extra verification: %v\n", localAddr, len(c.Config.Mldsa65Verify) > 0)
|
||||
}
|
||||
p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -280,35 +281,15 @@ func (c *Config) parseServerName() string {
|
||||
return c.ServerName
|
||||
}
|
||||
|
||||
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) (err error) {
|
||||
// extract x509 certificates from rawCerts(verifiedChains will be nil if InsecureSkipVerify is true)
|
||||
certs := make([]*x509.Certificate, len(rawCerts))
|
||||
for i, asn1Data := range rawCerts {
|
||||
certs[i], _ = x509.ParseCertificate(asn1Data)
|
||||
}
|
||||
|
||||
// directly return success if pinned cert is leaf
|
||||
// or add the CA to RootCAs if pinned cert is CA(and can be used in VerifyPeerCertInNames for Self signed CA)
|
||||
RootCAs := r.RootCAs
|
||||
if r.PinnedPeerCertSha256 != nil {
|
||||
verifyResult, verifiedCert := verifyChain(certs, r.PinnedPeerCertSha256)
|
||||
switch verifyResult {
|
||||
case certNotFound:
|
||||
return errors.New("peer cert is unrecognized")
|
||||
case foundLeaf:
|
||||
return nil
|
||||
case foundCA:
|
||||
RootCAs = x509.NewCertPool()
|
||||
RootCAs.AddCert(verifiedCert)
|
||||
default:
|
||||
panic("impossible PinnedPeerCertificateSha256 verify result")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
if r.VerifyPeerCertInNames != nil {
|
||||
if len(r.VerifyPeerCertInNames) > 0 {
|
||||
certs := make([]*x509.Certificate, len(rawCerts))
|
||||
for i, asn1Data := range rawCerts {
|
||||
certs[i], _ = x509.ParseCertificate(asn1Data)
|
||||
}
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: RootCAs,
|
||||
Roots: r.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
@@ -321,14 +302,42 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.PinnedPeerCertificateChainSha256 == nil {
|
||||
return errors.New("peer cert is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
if r.PinnedPeerCertificateChainSha256 != nil {
|
||||
hashValue := GenerateCertChainHash(rawCerts)
|
||||
for _, v := range r.PinnedPeerCertificateChainSha256 {
|
||||
if hmac.Equal(hashValue, v) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue))
|
||||
}
|
||||
|
||||
if r.PinnedPeerCertificatePublicKeySha256 != nil {
|
||||
for _, v := range verifiedChains {
|
||||
for _, cert := range v {
|
||||
publicHash := GenerateCertPublicKeyHash(cert)
|
||||
for _, c := range r.PinnedPeerCertificatePublicKeySha256 {
|
||||
if hmac.Equal(publicHash, c) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("peer public key is unrecognized.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RandCarrier struct {
|
||||
RootCAs *x509.CertPool
|
||||
VerifyPeerCertInNames []string
|
||||
PinnedPeerCertSha256 [][]byte
|
||||
RootCAs *x509.CertPool
|
||||
VerifyPeerCertInNames []string
|
||||
PinnedPeerCertificateChainSha256 [][]byte
|
||||
PinnedPeerCertificatePublicKeySha256 [][]byte
|
||||
}
|
||||
|
||||
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
||||
@@ -353,9 +362,10 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
}
|
||||
|
||||
randCarrier := &RandCarrier{
|
||||
RootCAs: root,
|
||||
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
||||
PinnedPeerCertSha256: c.PinnedPeerCertSha256,
|
||||
RootCAs: root,
|
||||
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
||||
PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256,
|
||||
PinnedPeerCertificatePublicKeySha256: c.PinnedPeerCertificatePublicKeySha256,
|
||||
}
|
||||
config := &tls.Config{
|
||||
Rand: randCarrier,
|
||||
@@ -516,28 +526,3 @@ func ParseCurveName(curveNames []string) []tls.CurveID {
|
||||
func IsFromMitm(str string) bool {
|
||||
return strings.ToLower(str) == "frommitm"
|
||||
}
|
||||
|
||||
type verifyResult int
|
||||
|
||||
const (
|
||||
certNotFound verifyResult = iota
|
||||
foundLeaf
|
||||
foundCA
|
||||
)
|
||||
|
||||
func verifyChain(certs []*x509.Certificate, PinnedPeerCertificateSha256 [][]byte) (verifyResult, *x509.Certificate) {
|
||||
for _, cert := range certs {
|
||||
certHash := GenerateCertHash(cert)
|
||||
for _, c := range PinnedPeerCertificateSha256 {
|
||||
if hmac.Equal(certHash, c) {
|
||||
if cert.IsCA {
|
||||
return foundCA, cert
|
||||
} else {
|
||||
return foundLeaf, cert
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return certNotFound, nil
|
||||
}
|
||||
|
||||
@@ -222,7 +222,6 @@ type Config struct {
|
||||
EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
||||
EchForceQuery string `protobuf:"bytes,20,opt,name=ech_force_query,json=echForceQuery,proto3" json:"ech_force_query,omitempty"`
|
||||
EchSocketSettings *internet.SocketConfig `protobuf:"bytes,21,opt,name=ech_socket_settings,json=echSocketSettings,proto3" json:"ech_socket_settings,omitempty"`
|
||||
PinnedPeerCertSha256 [][]byte `protobuf:"bytes,22,rep,name=pinned_peer_cert_sha256,json=pinnedPeerCertSha256,proto3" json:"pinned_peer_cert_sha256,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -395,13 +394,6 @@ func (x *Config) GetEchSocketSettings() *internet.SocketConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetPinnedPeerCertSha256() [][]byte {
|
||||
if x != nil {
|
||||
return x.PinnedPeerCertSha256
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
@@ -435,7 +427,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x45, 0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14,
|
||||
0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49,
|
||||
0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54,
|
||||
0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xa0, 0x08, 0x0a, 0x06, 0x43, 0x6f,
|
||||
0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xe9, 0x07, 0x0a, 0x06, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e,
|
||||
0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63,
|
||||
@@ -498,18 +490,15 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x52, 0x11, 0x65, 0x63, 0x68, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f,
|
||||
0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36,
|
||||
0x18, 0x16, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65,
|
||||
0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x42, 0x73, 0x0a, 0x1f,
|
||||
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50,
|
||||
0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f,
|
||||
0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c,
|
||||
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -101,6 +101,4 @@ message Config {
|
||||
string ech_force_query = 20;
|
||||
|
||||
SocketConfig ech_socket_settings = 21;
|
||||
|
||||
repeated bytes pinned_peer_cert_sha256 = 22;
|
||||
}
|
||||
|
||||
@@ -3,37 +3,40 @@ package tls
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
)
|
||||
|
||||
func CalculatePEMLeafCertSHA256Hash(certContent []byte) (string, error) {
|
||||
var leafCert *x509.Certificate
|
||||
func CalculatePEMCertChainSHA256Hash(certContent []byte) string {
|
||||
var certChain [][]byte
|
||||
for {
|
||||
var err error
|
||||
block, remain := pem.Decode(certContent)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
leafCert, err = x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
certChain = append(certChain, block.Bytes)
|
||||
certContent = remain
|
||||
}
|
||||
certHash := GenerateCertHash(leafCert)
|
||||
certHashHex := hex.EncodeToString(certHash)
|
||||
return certHashHex, nil
|
||||
certChainHash := GenerateCertChainHash(certChain)
|
||||
certChainHashB64 := base64.StdEncoding.EncodeToString(certChainHash)
|
||||
return certChainHashB64
|
||||
}
|
||||
|
||||
// []byte must be ASN.1 DER content
|
||||
func GenerateCertHash[T *x509.Certificate | []byte](cert T) []byte {
|
||||
var out [32]byte
|
||||
switch v := any(cert).(type) {
|
||||
case *x509.Certificate:
|
||||
out = sha256.Sum256(v.Raw)
|
||||
case []byte:
|
||||
out = sha256.Sum256(v)
|
||||
func GenerateCertChainHash(rawCerts [][]byte) []byte {
|
||||
var hashValue []byte
|
||||
for _, certValue := range rawCerts {
|
||||
out := sha256.Sum256(certValue)
|
||||
if hashValue == nil {
|
||||
hashValue = out[:]
|
||||
} else {
|
||||
newHashValue := sha256.Sum256(append(hashValue, out[:]...))
|
||||
hashValue = newHashValue[:]
|
||||
}
|
||||
}
|
||||
return hashValue
|
||||
}
|
||||
|
||||
func GenerateCertPublicKeyHash(cert *x509.Certificate) []byte {
|
||||
out := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
|
||||
return out[:]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package tls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
|
||||
@@ -10,88 +10,190 @@ import (
|
||||
)
|
||||
|
||||
func TestCalculateCertHash(t *testing.T) {
|
||||
const Single = `-----BEGIN CERTIFICATE-----
|
||||
MIINWzCCC0OgAwIBAgITMwK6ajqdrV0tahuIrQAAArpqOjANBgkqhkiG9w0BAQwF
|
||||
ADBdMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
|
||||
MS4wLAYDVQQDEyVNaWNyb3NvZnQgQXp1cmUgUlNBIFRMUyBJc3N1aW5nIENBIDA0
|
||||
MB4XDTI1MDkwOTEwMzE1NloXDTI2MDMwODEwMzE1NlowYzELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
|
||||
ZnQgQ29ycG9yYXRpb24xFTATBgNVBAMTDHd3dy5iaW5nLmNvbTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMBflymLifrVkjp8K4/XrHSt+/xDrrZIJyTI
|
||||
JOhIGZJZ88sNjo4OChQWV8O3CTQwrbKJDd6KjZFFc6BPKpEJZ891w2zkymMbE7wh
|
||||
vQVviSCIVCO+49pLrEvfh5ZvdbXhtNzm/ZRvkoI8h4ZKPBRNmX5sGpSQ9p0loJBj
|
||||
Jk1HbzLv0vRk5bLb/J6x7YexaAu86C9TjqnC4irO+AZZNI/0S70ZHxX+ETZVV0EX
|
||||
QU8UmqV68e4YhAQwiLYdAQw125n2hGWoLokQSZTyEiIIoubB00pE5zf0Qaq6Q4s8
|
||||
Go5Ukw1A4HjWMisHVKq369pgI8VDZtMzOhS+O0DEQZLwOFETZxECAwEAAaOCCQww
|
||||
ggkIMIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdgCWl2S/VViXrfdDh2g3CEJ3
|
||||
6fA61fak8zZuRqQ/D8qpxgAAAZkuEXLdAAAEAwBHMEUCIBLzX4AJgVJdQshSMBLS
|
||||
hBMQX8zgRm2U3IXjLk37JM3QAiEAkVrmCFx0+BM3NOoCAXBU1WzVuniPxJP3Ysbd
|
||||
OO3dkEAAdwBkEcRspBLsp4kcogIuALyrTygH1B41J6vq/tUDyX3N8AAAAZkuEXKd
|
||||
AAAEAwBIMEYCIQCCO1ys+tlI8Fhp4J/Dqk3VVtSi408Nuw8T6YciDL6LPgIhAPjp
|
||||
fm/gMkASgNimNuMFH8oiJbqeQ/yo2zQfub894iMuAHcAVmzVo3a+g9/jQrZ1xJwj
|
||||
JJinabrDgsurSaOHfZqzLQEAAAGZLhFy2QAABAMASDBGAiEA/93O6XiiYhfeANHh
|
||||
0n2nJyVvFAc6sBNT2S7WOR28vR0CIQC7i+leDRRIeY2BYJwaRlAqHlSyU4DZu5IG
|
||||
caxiWFeavzAnBgkrBgEEAYI3FQoEGjAYMAoGCCsGAQUFBwMCMAoGCCsGAQUFBwMB
|
||||
MDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIe91xuB5+tGgoGdLo7QDIfw2h1d
|
||||
gqvnMIft8R8CAWQCAS0wgbQGCCsGAQUFBwEBBIGnMIGkMHMGCCsGAQUFBzAChmdo
|
||||
dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUy
|
||||
MEF6dXJlJTIwUlNBJTIwVExTJTIwSXNzdWluZyUyMENBJTIwMDQlMjAtJTIweHNp
|
||||
Z24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQuY29t
|
||||
L29jc3AwHQYDVR0OBBYEFAsWImxddBew8yEv3yGDsmy90FzPMA4GA1UdDwEB/wQE
|
||||
AwIFoDCCBREGA1UdEQSCBQgwggUEghMqLnBsYXRmb3JtLmJpbmcuY29tggoqLmJp
|
||||
bmcuY29tgghiaW5nLmNvbYIWaWVvbmxpbmUubWljcm9zb2Z0LmNvbYITKi53aW5k
|
||||
b3dzc2VhcmNoLmNvbYIZY24uaWVvbmxpbmUubWljcm9zb2Z0LmNvbYIRKi5vcmln
|
||||
aW4uYmluZy5jb22CDSoubW0uYmluZy5uZXSCDiouYXBpLmJpbmcuY29tgg0qLmNu
|
||||
LmJpbmcubmV0gg0qLmNuLmJpbmcuY29tghBzc2wtYXBpLmJpbmcuY29tghBzc2wt
|
||||
YXBpLmJpbmcubmV0gg4qLmFwaS5iaW5nLm5ldIIOKi5iaW5nYXBpcy5jb22CD2Jp
|
||||
bmdzYW5kYm94LmNvbYIWZmVlZGJhY2subWljcm9zb2Z0LmNvbYIbaW5zZXJ0bWVk
|
||||
aWEuYmluZy5vZmZpY2UubmV0gg5yLmJhdC5iaW5nLmNvbYIQKi5yLmJhdC5iaW5n
|
||||
LmNvbYIPKi5kaWN0LmJpbmcuY29tgg4qLnNzbC5iaW5nLmNvbYIQKi5hcHBleC5i
|
||||
aW5nLmNvbYIWKi5wbGF0Zm9ybS5jbi5iaW5nLmNvbYINd3AubS5iaW5nLmNvbYIM
|
||||
Ki5tLmJpbmcuY29tgg9nbG9iYWwuYmluZy5jb22CEXdpbmRvd3NzZWFyY2guY29t
|
||||
gg5zZWFyY2gubXNuLmNvbYIRKi5iaW5nc2FuZGJveC5jb22CGSouYXBpLnRpbGVz
|
||||
LmRpdHUubGl2ZS5jb22CGCoudDAudGlsZXMuZGl0dS5saXZlLmNvbYIYKi50MS50
|
||||
aWxlcy5kaXR1LmxpdmUuY29tghgqLnQyLnRpbGVzLmRpdHUubGl2ZS5jb22CGCou
|
||||
dDMudGlsZXMuZGl0dS5saXZlLmNvbYILM2QubGl2ZS5jb22CE2FwaS5zZWFyY2gu
|
||||
bGl2ZS5jb22CFGJldGEuc2VhcmNoLmxpdmUuY29tghVjbndlYi5zZWFyY2gubGl2
|
||||
ZS5jb22CDWRpdHUubGl2ZS5jb22CEWZhcmVjYXN0LmxpdmUuY29tgg5pbWFnZS5s
|
||||
aXZlLmNvbYIPaW1hZ2VzLmxpdmUuY29tghFsb2NhbC5saXZlLmNvbS5hdYIUbG9j
|
||||
YWxzZWFyY2gubGl2ZS5jb22CFGxzNGQuc2VhcmNoLmxpdmUuY29tgg1tYWlsLmxp
|
||||
dmUuY29tghFtYXBpbmRpYS5saXZlLmNvbYIObG9jYWwubGl2ZS5jb22CDW1hcHMu
|
||||
bGl2ZS5jb22CEG1hcHMubGl2ZS5jb20uYXWCD21pbmRpYS5saXZlLmNvbYINbmV3
|
||||
cy5saXZlLmNvbYIcb3JpZ2luLmNud2ViLnNlYXJjaC5saXZlLmNvbYIWcHJldmll
|
||||
dy5sb2NhbC5saXZlLmNvbYIPc2VhcmNoLmxpdmUuY29tghJ0ZXN0Lm1hcHMubGl2
|
||||
ZS5jb22CDnZpZGVvLmxpdmUuY29tgg92aWRlb3MubGl2ZS5jb22CFXZpcnR1YWxl
|
||||
YXJ0aC5saXZlLmNvbYIMd2FwLmxpdmUuY29tghJ3ZWJtYXN0ZXIubGl2ZS5jb22C
|
||||
FXd3dy5sb2NhbC5saXZlLmNvbS5hdYIUd3d3Lm1hcHMubGl2ZS5jb20uYXWCE3dl
|
||||
Ym1hc3RlcnMubGl2ZS5jb22CGGVjbi5kZXYudmlydHVhbGVhcnRoLm5ldIIMd3d3
|
||||
LmJpbmcuY29tMAwGA1UdEwEB/wQCMAAwagYDVR0fBGMwYTBfoF2gW4ZZaHR0cDov
|
||||
L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwQXp1cmUl
|
||||
MjBSU0ElMjBUTFMlMjBJc3N1aW5nJTIwQ0ElMjAwNC5jcmwwZgYDVR0gBF8wXTBR
|
||||
BgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3Nv
|
||||
ZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMAgGBmeBDAECAjAfBgNV
|
||||
HSMEGDAWgBQ7cNFT6XYlnWCoymYPxpuub1QWajAdBgNVHSUEFjAUBggrBgEFBQcD
|
||||
AgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEMBQADggIBAEQCoppNllgoHtfLJt2m7cVL
|
||||
AILYFxJdi9qc4LUBfaQEdUwAfsC1pSk5YFB0aGcmVFKMvMMOeENOrWgNJVTLYI05
|
||||
8mu6XmbiqUeIu1Rlye/yNirYm33Js2f3VXYp6HSzisF5cWq4QwYqA6XIMfDl61/y
|
||||
IXVb5l5eTfproM2grn3RcVVbk5DuEUfyDPzYYNm8elxzac4RrbkDif/b+tVFxmrJ
|
||||
CUx1o3VLiVVzbIFCDc5r6pPArm1EdgseJ7pRdXzg6flwA0INRpeLCpjtvkHeZCh7
|
||||
GS2JUBhFv7M+lneJljNU/trTkYiho+ZRW9AgLcN73c4+1wHttPHk+w19m5Ge182V
|
||||
HzCQdO27IGovKN8jkprGafGxYhyCn4KdSYbRrG7fjkckzpJrjCpF2/bJJ+o4Zi9P
|
||||
rJIKHzY5lIMXcD7wwwT2WwlKXoTDrgm4QKN18V+kZaoOILdKyMlEww4jPFUqk6j1
|
||||
0Qeod55F5h4tCq2lmwDIa/jyWTGgqTr4UESqj46NB5+JkGYl0O1PPbS1nUm9sN1l
|
||||
hkY45iskXVXqLl6AVVcXyxMTefD43M81tFVuJJgpdD/BaMaXAuBdNDfTQcJwhP99
|
||||
uI6HqHFD3iEct8fBkYfQiwH2e1eu9OwgujiWHsutyK8VvzVB3/YnhQ/TzciRjPqz
|
||||
7ykUutQNUALq8dQwoTnK
|
||||
/* This is used to make sure that the hash signature generated is consistent
|
||||
Do NOT change this test to suit your modification.
|
||||
*/
|
||||
const CertBundle = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFJjCCBA6gAwIBAgISBL8FgUdEcVYEjdMkTZPgC3ocMA0GCSqGSIb3DQEBCwUA
|
||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
|
||||
EwJSMzAeFw0yMTAzMjkwMTM2MzlaFw0yMTA2MjcwMTM2MzlaMBsxGTAXBgNVBAMT
|
||||
EHNlY3VyZS5ra2Rldi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDOF/j+s7rHaDMXdhYjffoOFjNZb7n3sCuvubI3qOcgJmr1WPlCEry50KoY8FaB
|
||||
IF2HstMIZceN4NoUK7mr3WAvsQTA47uBfuhp+XQmAQW0T/fYD+XbvxtCENEin+xm
|
||||
JsvTKZLTKbE08E964J4H+1sBmueP6rvy2Wt95z0XkqoQiikpmLE87WdltQcATvVX
|
||||
qqrL64hV0nN4Hdi2Bv1cQ92aR7lZGj8jiQRtTj8y5Ah3Gk3fPoao+yI7gnzembqo
|
||||
fddePzz/u8iEuvYAsIYZKn9bbS7rkYoJazL2/xiDZR7usn0SomzmM6lGXDD3FF4b
|
||||
lyTkLYwgFVgbGWoz1+eOHD5BAgMBAAGjggJLMIICRzAOBgNVHQ8BAf8EBAMCBaAw
|
||||
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD
|
||||
VR0OBBYEFOPRdApL8XENLXDuU3oPisykGyp+MB8GA1UdIwQYMBaAFBQusxe3WFbL
|
||||
rlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDov
|
||||
L3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5v
|
||||
cmcvMBsGA1UdEQQUMBKCEHNlY3VyZS5ra2Rldi5vcmcwTAYDVR0gBEUwQzAIBgZn
|
||||
gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
|
||||
ZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQD2XJQv0Xcw
|
||||
IhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXh71yBGAAAEAwBGMEQCIDmziDOn
|
||||
ehPY2KoAFX8fPWiCm4EBTbGJXBWF1LCotPJBAiBLSCg+krXvbyoABnTm8knv0hbG
|
||||
/ZOk8LV6qpw9VoQwGwB3AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kT
|
||||
AAABeHvXIIAAAAQDAEgwRgIhAOkeKc52wR3n5QWZfa3zbbicMMSQrTGbQ+1fHNs7
|
||||
SsRvAiEAqbflDx1nZRsTA22FfNYfgF6v5Z3/svjiTleWSQad4WswDQYJKoZIhvcN
|
||||
AQELBQADggEBAEj8tg+Agf5sNBM9CvjeXbA0fkpGDaQzXEkwefAea5vPgKoGiWSN
|
||||
pHDkyr0i7+mqa7cMXhmmo20V0/+QDv0nrsCw8pgJuviC3GvK6agT6WfkXM2djJuo
|
||||
osPeXOL9KEF/ATv0EyM5tr9TIoRSSYQoRhuqEdg3Dz9Ii8GXR5HhbYr6Cd7gwNHS
|
||||
kYeivKDmgv31GHb4twPSE9LZ/U+56lNVvSbJ4csupIF3GnxnxrFSmijYNOPoM6mj
|
||||
tzY45d4mjPs0fKCFKSsVM6YT0tX4NwIKsOaeQg30WLtRyDwYm6ma/a/UUUS0FloZ
|
||||
2/p85glOgzownfoRjzTbqHu8ewtMd6Apc0E=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
|
||||
MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
|
||||
AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
|
||||
jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
|
||||
Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
|
||||
U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
|
||||
gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
|
||||
/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
|
||||
oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
|
||||
BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
|
||||
ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
|
||||
p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
|
||||
AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
|
||||
Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
|
||||
LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
|
||||
r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
|
||||
ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
|
||||
S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
|
||||
qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
|
||||
O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
|
||||
UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
t.Run("bundle", func(t *testing.T) {
|
||||
hash := CalculatePEMCertChainSHA256Hash([]byte(CertBundle))
|
||||
assert.Equal(t, "WF65fBkgltadMnXryOMZ6TEYeV4d5Q0uu4SGXGZ0RjI=", hash)
|
||||
})
|
||||
const Single = `-----BEGIN CERTIFICATE-----
|
||||
MIIFJjCCBA6gAwIBAgISBL8FgUdEcVYEjdMkTZPgC3ocMA0GCSqGSIb3DQEBCwUA
|
||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
|
||||
EwJSMzAeFw0yMTAzMjkwMTM2MzlaFw0yMTA2MjcwMTM2MzlaMBsxGTAXBgNVBAMT
|
||||
EHNlY3VyZS5ra2Rldi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDOF/j+s7rHaDMXdhYjffoOFjNZb7n3sCuvubI3qOcgJmr1WPlCEry50KoY8FaB
|
||||
IF2HstMIZceN4NoUK7mr3WAvsQTA47uBfuhp+XQmAQW0T/fYD+XbvxtCENEin+xm
|
||||
JsvTKZLTKbE08E964J4H+1sBmueP6rvy2Wt95z0XkqoQiikpmLE87WdltQcATvVX
|
||||
qqrL64hV0nN4Hdi2Bv1cQ92aR7lZGj8jiQRtTj8y5Ah3Gk3fPoao+yI7gnzembqo
|
||||
fddePzz/u8iEuvYAsIYZKn9bbS7rkYoJazL2/xiDZR7usn0SomzmM6lGXDD3FF4b
|
||||
lyTkLYwgFVgbGWoz1+eOHD5BAgMBAAGjggJLMIICRzAOBgNVHQ8BAf8EBAMCBaAw
|
||||
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD
|
||||
VR0OBBYEFOPRdApL8XENLXDuU3oPisykGyp+MB8GA1UdIwQYMBaAFBQusxe3WFbL
|
||||
rlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDov
|
||||
L3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5v
|
||||
cmcvMBsGA1UdEQQUMBKCEHNlY3VyZS5ra2Rldi5vcmcwTAYDVR0gBEUwQzAIBgZn
|
||||
gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
|
||||
ZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQD2XJQv0Xcw
|
||||
IhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXh71yBGAAAEAwBGMEQCIDmziDOn
|
||||
ehPY2KoAFX8fPWiCm4EBTbGJXBWF1LCotPJBAiBLSCg+krXvbyoABnTm8knv0hbG
|
||||
/ZOk8LV6qpw9VoQwGwB3AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kT
|
||||
AAABeHvXIIAAAAQDAEgwRgIhAOkeKc52wR3n5QWZfa3zbbicMMSQrTGbQ+1fHNs7
|
||||
SsRvAiEAqbflDx1nZRsTA22FfNYfgF6v5Z3/svjiTleWSQad4WswDQYJKoZIhvcN
|
||||
AQELBQADggEBAEj8tg+Agf5sNBM9CvjeXbA0fkpGDaQzXEkwefAea5vPgKoGiWSN
|
||||
pHDkyr0i7+mqa7cMXhmmo20V0/+QDv0nrsCw8pgJuviC3GvK6agT6WfkXM2djJuo
|
||||
osPeXOL9KEF/ATv0EyM5tr9TIoRSSYQoRhuqEdg3Dz9Ii8GXR5HhbYr6Cd7gwNHS
|
||||
kYeivKDmgv31GHb4twPSE9LZ/U+56lNVvSbJ4csupIF3GnxnxrFSmijYNOPoM6mj
|
||||
tzY45d4mjPs0fKCFKSsVM6YT0tX4NwIKsOaeQg30WLtRyDwYm6ma/a/UUUS0FloZ
|
||||
2/p85glOgzownfoRjzTbqHu8ewtMd6Apc0E=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
t.Run("single", func(t *testing.T) {
|
||||
hash := CalculatePEMCertChainSHA256Hash([]byte(Single))
|
||||
assert.Equal(t, "FW3SVMCL6um2wVltOdgJ3DpI82aredw83YoCblkMkVM=", hash)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCalculateCertPublicKeyHash(t *testing.T) {
|
||||
const Single = `-----BEGIN CERTIFICATE-----
|
||||
MIINWTCCC0GgAwIBAgITLQAxbA/A+lw/1sLDAAAAADFsDzANBgkqhkiG9w0BAQsF
|
||||
ADBPMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
|
||||
MSAwHgYDVQQDExdNaWNyb3NvZnQgUlNBIFRMUyBDQSAwMjAeFw0yMjExMjUwMDU2
|
||||
NTZaFw0yMzA1MjUwMDU2NTZaMBcxFTATBgNVBAMTDHd3dy5iaW5nLmNvbTCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOH89lKmtkDnClFiQwfZofZO4h8C
|
||||
Ye/+ChI67pEw5Q6/MxJzHiMKe8f1WaNuc+wkdHdct+BmQ+AftozIJt+eSN6IF7eY
|
||||
dsutBvR87GNLFe40MBvfyvTQVM9Ulv04JxOpKTYnsf2wmktEI3y7FCgfm9RT71n+
|
||||
Zef8Z8fa4By7aGfbbCQ0DsHl5P9o3ug/eLQODzK9NuQlwcVBHD2Zvgo+K7WOsjgE
|
||||
k8JnOr+2zc0WWT4OrWSDJE/3l+jvhxmZkrwgmks4m9zUZvAnYAz/xxVCJRqbI3Ou
|
||||
S5fkJJ3f6IxPbS2i8OWz6tma1aIkgQaFNJQuYOJa1esfQcEzs6kb/Xx5DXUCAwEA
|
||||
AaOCCWQwgglgMIIBfQYKKwYBBAHWeQIEAgSCAW0EggFpAWcAdgCt9776fP8QyIud
|
||||
PZwePhhqtGcpXc+xDCTKhYY069yCigAAAYSsUtxtAAAEAwBHMEUCIQCP/Jpp337p
|
||||
cKITqS/kNlA4bNY6TK1Ad0VlsdkzQU+oZgIgFZb2AcsyT1UKCmM3ziGsLdvS9MAT
|
||||
D1g/kztyDXhkA70AdgBVgdTCFpA2AUrqC5tXPFPwwOQ4eHAlCBcvo6odBxPTDAAA
|
||||
AYSsUtsZAAAEAwBHMEUCIQDvlqXrdA440PW6b+JLj4F0ZVQNKHcv1lub0FhQqHgR
|
||||
wAIgAtC7eXvXXhVBuO+Bd3fkDI0aGQM+pcvIesBoygzStjQAdQB6MoxU2LcttiDq
|
||||
OOBSHumEFnAyE4VNO9IrwTpXo1LrUgAAAYSsUtmfAAAEAwBGMEQCIDgjSYt6e/h8
|
||||
dv2KGEL3AJZUBH2gp1AA5saH8o3OyMJhAiBOCzo3oWlVFeF/8c0fxIIs9Fj4w8BY
|
||||
INo0jNP/k7apgTAnBgkrBgEEAYI3FQoEGjAYMAoGCCsGAQUFBwMBMAoGCCsGAQUF
|
||||
BwMCMD4GCSsGAQQBgjcVBwQxMC8GJysGAQQBgjcVCIfahnWD7tkBgsmFG4G1nmGF
|
||||
9OtggV2Fho5Bh8KYUAIBZAIBJzCBhwYIKwYBBQUHAQEEezB5MFMGCCsGAQUFBzAC
|
||||
hkdodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL21zY29ycC9NaWNyb3NvZnQl
|
||||
MjBSU0ElMjBUTFMlMjBDQSUyMDAyLmNydDAiBggrBgEFBQcwAYYWaHR0cDovL29j
|
||||
c3AubXNvY3NwLmNvbTAdBgNVHQ4EFgQUpuSPPchFlPGu8FTbzPhJTFxQ7RowDgYD
|
||||
VR0PAQH/BAQDAgSwMIIFbQYDVR0RBIIFZDCCBWCCDHd3dy5iaW5nLmNvbYIQZGlj
|
||||
dC5iaW5nLmNvbS5jboITKi5wbGF0Zm9ybS5iaW5nLmNvbYIKKi5iaW5nLmNvbYII
|
||||
YmluZy5jb22CFmllb25saW5lLm1pY3Jvc29mdC5jb22CEyoud2luZG93c3NlYXJj
|
||||
aC5jb22CGWNuLmllb25saW5lLm1pY3Jvc29mdC5jb22CESoub3JpZ2luLmJpbmcu
|
||||
Y29tgg0qLm1tLmJpbmcubmV0gg4qLmFwaS5iaW5nLmNvbYIYZWNuLmRldi52aXJ0
|
||||
dWFsZWFydGgubmV0gg0qLmNuLmJpbmcubmV0gg0qLmNuLmJpbmcuY29tghBzc2wt
|
||||
YXBpLmJpbmcuY29tghBzc2wtYXBpLmJpbmcubmV0gg4qLmFwaS5iaW5nLm5ldIIO
|
||||
Ki5iaW5nYXBpcy5jb22CD2JpbmdzYW5kYm94LmNvbYIWZmVlZGJhY2subWljcm9z
|
||||
b2Z0LmNvbYIbaW5zZXJ0bWVkaWEuYmluZy5vZmZpY2UubmV0gg5yLmJhdC5iaW5n
|
||||
LmNvbYIQKi5yLmJhdC5iaW5nLmNvbYISKi5kaWN0LmJpbmcuY29tLmNugg8qLmRp
|
||||
Y3QuYmluZy5jb22CDiouc3NsLmJpbmcuY29tghAqLmFwcGV4LmJpbmcuY29tghYq
|
||||
LnBsYXRmb3JtLmNuLmJpbmcuY29tgg13cC5tLmJpbmcuY29tggwqLm0uYmluZy5j
|
||||
b22CD2dsb2JhbC5iaW5nLmNvbYIRd2luZG93c3NlYXJjaC5jb22CDnNlYXJjaC5t
|
||||
c24uY29tghEqLmJpbmdzYW5kYm94LmNvbYIZKi5hcGkudGlsZXMuZGl0dS5saXZl
|
||||
LmNvbYIPKi5kaXR1LmxpdmUuY29tghgqLnQwLnRpbGVzLmRpdHUubGl2ZS5jb22C
|
||||
GCoudDEudGlsZXMuZGl0dS5saXZlLmNvbYIYKi50Mi50aWxlcy5kaXR1LmxpdmUu
|
||||
Y29tghgqLnQzLnRpbGVzLmRpdHUubGl2ZS5jb22CFSoudGlsZXMuZGl0dS5saXZl
|
||||
LmNvbYILM2QubGl2ZS5jb22CE2FwaS5zZWFyY2gubGl2ZS5jb22CFGJldGEuc2Vh
|
||||
cmNoLmxpdmUuY29tghVjbndlYi5zZWFyY2gubGl2ZS5jb22CDGRldi5saXZlLmNv
|
||||
bYINZGl0dS5saXZlLmNvbYIRZmFyZWNhc3QubGl2ZS5jb22CDmltYWdlLmxpdmUu
|
||||
Y29tgg9pbWFnZXMubGl2ZS5jb22CEWxvY2FsLmxpdmUuY29tLmF1ghRsb2NhbHNl
|
||||
YXJjaC5saXZlLmNvbYIUbHM0ZC5zZWFyY2gubGl2ZS5jb22CDW1haWwubGl2ZS5j
|
||||
b22CEW1hcGluZGlhLmxpdmUuY29tgg5sb2NhbC5saXZlLmNvbYINbWFwcy5saXZl
|
||||
LmNvbYIQbWFwcy5saXZlLmNvbS5hdYIPbWluZGlhLmxpdmUuY29tgg1uZXdzLmxp
|
||||
dmUuY29tghxvcmlnaW4uY253ZWIuc2VhcmNoLmxpdmUuY29tghZwcmV2aWV3Lmxv
|
||||
Y2FsLmxpdmUuY29tgg9zZWFyY2gubGl2ZS5jb22CEnRlc3QubWFwcy5saXZlLmNv
|
||||
bYIOdmlkZW8ubGl2ZS5jb22CD3ZpZGVvcy5saXZlLmNvbYIVdmlydHVhbGVhcnRo
|
||||
LmxpdmUuY29tggx3YXAubGl2ZS5jb22CEndlYm1hc3Rlci5saXZlLmNvbYITd2Vi
|
||||
bWFzdGVycy5saXZlLmNvbYIVd3d3LmxvY2FsLmxpdmUuY29tLmF1ghR3d3cubWFw
|
||||
cy5saXZlLmNvbS5hdTCBsAYDVR0fBIGoMIGlMIGioIGfoIGchk1odHRwOi8vbXNj
|
||||
cmwubWljcm9zb2Z0LmNvbS9wa2kvbXNjb3JwL2NybC9NaWNyb3NvZnQlMjBSU0El
|
||||
MjBUTFMlMjBDQSUyMDAyLmNybIZLaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3Br
|
||||
aS9tc2NvcnAvY3JsL01pY3Jvc29mdCUyMFJTQSUyMFRMUyUyMENBJTIwMDIuY3Js
|
||||
MFcGA1UdIARQME4wQgYJKwYBBAGCNyoBMDUwMwYIKwYBBQUHAgEWJ2h0dHA6Ly93
|
||||
d3cubWljcm9zb2Z0LmNvbS9wa2kvbXNjb3JwL2NwczAIBgZngQwBAgEwHwYDVR0j
|
||||
BBgwFoAU/y9/4Qb0OPMt7SWNmML+DvZs/PowHQYDVR0lBBYwFAYIKwYBBQUHAwEG
|
||||
CCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQB4OIB/EHxpF64iFZME7XkJjZYn
|
||||
ZiYIfOfHs6EGDNn7fxvpZS9HVy1jOWv/RvzEbMuSV3b/fItaJN/zATBg5/6hb5Jq
|
||||
HGIcnKmb+tYrKlYhSOngHSu/8/OYP1dFFIqcVe0769kwXaKUzLh6UVRaS+mB7GFc
|
||||
sXmPMbv5NM7mCUEdMkOaoSmubfw/WzmmRGrcSmtCxtIwMcp8Jf13Esunq//4+9w3
|
||||
M/JXa8ubmXyrY63zt/Oz/NkVJvja89ueovscy6s5sw2r+Su4bRsJjmxwCbakp56K
|
||||
rbh7z417LzW88MMuATvOyk/O8Rbw2KYVSEiQgO54kHI0YkHkJ/6IoeAT1pmCfHUE
|
||||
Rd+Ec8T+/lE2BPLVqp8SjogDYiybb0IR5Gn2vYyUdzsS2h/C5qGNd2t5ehxfjQoL
|
||||
G6Y3GJZQRxkSX6TLPYU0U63wWb4yeSxabpBlARaZMaAoqDa3cX53WCnrAXDz8vuH
|
||||
yAtX2/Jq7IpybFK5kFzbxfI02Ik0aCWJUnXPL8L6esTskwvkzX8rSI/bjPrzcJL5
|
||||
B9pONLy6wc8/Arfu2eNlMbs8s/g8c5zkEc3fBZ9tJ1dqlnMAVgB2+fwI3aK4F34N
|
||||
uyfZW7Xu65KkPhbMnO0GVGM7X4Lkyjm4ysQ9PIRV3MwMfXH+RBSXlIayLTcYG4gl
|
||||
XF1a/qnao6nMjyTIyQ==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
t.Run("singlepublickey", func(t *testing.T) {
|
||||
block, _ := pem.Decode([]byte(Single))
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
assert.Equal(t, err, nil)
|
||||
hash := GenerateCertHash(cert)
|
||||
fingerprint, _ := hex.DecodeString("ae243d668ec9c7f74a0dcd1ad21c6676b4efe30c39728934b362093af886bf77")
|
||||
assert.Equal(t, fingerprint, hash)
|
||||
hash := GenerateCertPublicKeyHash(cert)
|
||||
hashstr := base64.StdEncoding.EncodeToString(hash)
|
||||
assert.Equal(t, "xI/4mNm8xF9uDT4vA9G1+aKAaybwNlkRECnN8vGAHTM=", hashstr)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user