securechannel
import "github.com/TheManticoreProject/Manticore/windows/protocols/ms-nrpc/securechannel"
Package securechannel implements the Netlogon secure channel ([MS-NRPC] 3.1.4): the challenge/response handshake and session establishment, the rolling per-call authenticator, the per-message sign/seal tokens, and the adapter that lets the DCE/RPC client use Netlogon as its own security provider (RPC_C_AUTHN_NETLOGON). It builds on the cryptographic primitives in the sibling crypto package and the NDR structures in the parent package, and invokes the interface opnums to run the handshake.
Index
- Constants
- type MessageSecurity
- func NewMessageSecurityAES(sessionKey [16]byte) *MessageSecurity
- func NewMessageSecurityRC4(sessionKey [16]byte) *MessageSecurity
- func (m *MessageSecurity) Seal(data []byte) (sealed, token []byte, err error)
- func (m *MessageSecurity) Sign(data []byte) ([]byte, error)
- func (m *MessageSecurity) Unseal(sealed, token []byte) ([]byte, error)
- func (m *MessageSecurity) VerifySignature(data, token []byte) error
- type NetlogonSecurityContext
- func NewNetlogonSecurityContext(sc *SecureChannel) *NetlogonSecurityContext
- func (n *NetlogonSecurityContext) AuthValueLen(seal bool, _ int) int
- func (n *NetlogonSecurityContext) ProtectRequest(_, stub []byte, seal bool) ([]byte, []byte, error)
- func (n *NetlogonSecurityContext) UnprotectResponse(_, stub, authValue []byte, seal bool) ([]byte, error)
- type SecureChannel
- func Establish(rpc ndr.Invoker, cfg SecureChannelConfig) (*SecureChannel, error)
- func (s *SecureChannel) NegotiateFlags() uint32
- func (s *SecureChannel) NextAuthenticator() msnrpc.NETLOGON_AUTHENTICATOR
- func (s *SecureChannel) SessionKey() [16]byte
- func (s *SecureChannel) UsesAES() bool
- func (s *SecureChannel) VerifyResponseAuthenticator(server msnrpc.NETLOGON_AUTHENTICATOR) error
- type SecureChannelConfig
Constants
DefaultNegotiateFlags is a capable client negotiate-flag set ([MS-NRPC] 3.1.4.2) that advertises AES support. Establish derives the cipher suite from the flags it is given: with NegotiateAES set (as here) it uses the AES suite; with that bit cleared it falls back to the legacy strong-key (RC4/DES) suite.
const DefaultNegotiateFlags uint32 = 0x212fffff
type MessageSecurity
MessageSecurity produces and verifies the per-message Netlogon security tokens used when Netlogon acts as its own RPC security provider (RPC_C_AUTHN_NETLOGON) ([MS-NRPC] 3.3.4.2.1). With AES negotiated it emits NL_AUTH_SHA2_SIGNATURE tokens (HMAC-SHA256 + AES-128-CFB8); otherwise it emits legacy NL_AUTH_SIGNATURE tokens (HMAC-MD5 + RC4). Both carry a checksum, a per-message sequence number, and — when sealing — a confounder.
A MessageSecurity is stateful and not safe for concurrent use: the client sequence number advances with every Sign/Seal call and calls must be serialized in the order the PDUs go on the wire.
type MessageSecurity struct {
// contains filtered or unexported fields
}
func NewMessageSecurityAES
func NewMessageSecurityAES(sessionKey [16]byte) *MessageSecurity
NewMessageSecurityAES returns a MessageSecurity for the AES cipher suite, keyed by the 16-byte session key derived by crypto.ComputeSessionKeyAES. The sequence number starts at zero.
func NewMessageSecurityRC4
func NewMessageSecurityRC4(sessionKey [16]byte) *MessageSecurity
NewMessageSecurityRC4 returns a MessageSecurity for the legacy (non-AES) cipher suite, keyed by the 16-byte session key derived by crypto.ComputeSessionKeyStrongKey.
func (*MessageSecurity) Seal
func (m *MessageSecurity) Seal(data []byte) (sealed, token []byte, err error)
Seal builds a sealing token over data ([MS-NRPC] 3.3.4.2.1 with Confidentiality requested) and returns the encrypted stub alongside it. The checksum is computed over the plaintext (header, confounder, stub); the confounder and stub are then encrypted. The client sequence number is consumed and advanced.
func (*MessageSecurity) Sign
func (m *MessageSecurity) Sign(data []byte) ([]byte, error)
Sign builds an integrity-only token over data ([MS-NRPC] 3.3.4.2.1 with Confidentiality not requested): the checksum covers the token header and the plaintext stub, no confounder is included, and data is left in the clear. The client sequence number is consumed and advanced.
func (*MessageSecurity) Unseal
func (m *MessageSecurity) Unseal(sealed, token []byte) ([]byte, error)
Unseal reverses Seal: it decrypts the sealed stub using the token’s own encrypted sequence number and confounder, then recomputes and verifies the checksum over the recovered plaintext. It is self-contained (the sequence number is recovered from the token), so a Seal round-trips through Unseal under the same session key.
func (*MessageSecurity) VerifySignature
func (m *MessageSecurity) VerifySignature(data, token []byte) error
VerifySignature checks an integrity-only token against data by recomputing the checksum.
type NetlogonSecurityContext
NetlogonSecurityContext adapts the Netlogon per-message MessageSecurity to the DCE/RPC client’s SecurityContext, so a connection can protect its PDUs with RPC_C_AUTHN_NETLOGON (auth_type 0x44). Unlike NTLM, Netlogon signs and seals only the stub, not the whole PDU ([MS-NRPC] 3.3.4.2.1, confirmed against Windows: the token covers pduData, not the RPC header or sec_trailer), so both directions ignore the client’s signedRegion argument and operate on the stub. A context is stateful (its sequence number advances per request) and must not be shared across connections.
type NetlogonSecurityContext struct {
// contains filtered or unexported fields
}
func NewNetlogonSecurityContext
func NewNetlogonSecurityContext(sc *SecureChannel) *NetlogonSecurityContext
NewNetlogonSecurityContext builds an RPC security provider from a secure channel: the cipher suite (AES vs legacy RC4) follows the channel, and the session key is taken from it. It is passed to the client’s SetAuthProvider together with the NL_AUTH_MESSAGE bind token.
func (*NetlogonSecurityContext) AuthValueLen
func (n *NetlogonSecurityContext) AuthValueLen(seal bool, _ int) int
AuthValueLen is the token length: AES (NL_AUTH_SHA2_SIGNATURE) 56 sealing / 48 signing; legacy (NL_AUTH_SIGNATURE) 32 sealing / 24 signing. The Netlogon token is a fixed size, so the stub length is not consulted.
func (*NetlogonSecurityContext) ProtectRequest
func (n *NetlogonSecurityContext) ProtectRequest(_, stub []byte, seal bool) ([]byte, []byte, error)
ProtectRequest seals (privacy) or signs (integrity) the request stub, returning the on-wire stub and the token.
func (*NetlogonSecurityContext) UnprotectResponse
func (n *NetlogonSecurityContext) UnprotectResponse(_, stub, authValue []byte, seal bool) ([]byte, error)
UnprotectResponse unseals (privacy) or verifies (integrity) the response stub against its token, returning the recovered plaintext stub.
type SecureChannel
SecureChannel is an established Netlogon secure channel ([MS-NRPC] 3.1.4): it holds the negotiated session key, the negotiate flags, the cipher suite, and the rolling stored credential used to compute and verify per-call Netlogon authenticators (3.1.4.5).
A SecureChannel is stateful and not safe for concurrent use: the stored credential advances on every NextAuthenticator/VerifyResponseAuthenticator call, and the two must be paired around each authenticated request in the order the calls go on the wire.
type SecureChannel struct {
// contains filtered or unexported fields
}
func Establish
func Establish(rpc ndr.Invoker, cfg SecureChannelConfig) (*SecureChannel, error)
Establish runs the Netlogon secure-channel handshake over the bound RPC connection rpc ([MS-NRPC] 3.1.4.1): it generates a client challenge, exchanges it for the server challenge via NetrServerReqChallenge (opnum 4), derives the session key, and proves possession of the machine secret via NetrServerAuthenticate3 (opnum 26). The cipher suite (AES vs legacy strong-key) follows the NegotiateAES bit in the negotiate flags. It then verifies the server’s returned credential equals the computed credential of the server challenge before returning the channel; a mismatch means the server did not prove knowledge of the shared secret and the channel is rejected.
rpc must already be bound to the Netlogon interface (an anonymous/unauthenticated binding is sufficient for the handshake itself).
func (*SecureChannel) NegotiateFlags
func (s *SecureChannel) NegotiateFlags() uint32
NegotiateFlags returns the negotiate flags the server agreed to (its echoed subset).
func (*SecureChannel) NextAuthenticator
func (s *SecureChannel) NextAuthenticator() msnrpc.NETLOGON_AUTHENTICATOR
NextAuthenticator computes the client authenticator to send with the next authenticated request ([MS-NRPC] 3.1.4.5 step 1): it advances the stored credential by the current timestamp and encrypts the result under the session key. It mutates the channel state, so each call yields the authenticator for exactly one request and must be followed by a VerifyResponseAuthenticator on the server’s reply.
func (*SecureChannel) SessionKey
func (s *SecureChannel) SessionKey() [16]byte
SessionKey returns the negotiated 16-byte session key.
func (*SecureChannel) UsesAES
func (s *SecureChannel) UsesAES() bool
UsesAES reports whether the AES cipher suite was negotiated; it selects the matching MessageSecurity (AES vs RC4) for RPC-level sealing.
func (*SecureChannel) VerifyResponseAuthenticator
func (s *SecureChannel) VerifyResponseAuthenticator(server msnrpc.NETLOGON_AUTHENTICATOR) error
VerifyResponseAuthenticator validates the server’s return authenticator ([MS-NRPC] 3.1.4.5 step 3): it advances the stored credential by one and checks that encrypting it under the session key reproduces the server’s credential, in constant time. A mismatch means the secure channel is no longer valid and the caller should re-establish it. It must be called once per request, paired with the preceding NextAuthenticator.
type SecureChannelConfig
SecureChannelConfig holds the inputs to Establish. Exactly one of Password or NTHash supplies the machine-account secret: NTHash (the raw 16-byte NT hash) takes precedence when non-nil, otherwise the NT one-way function of Password is used.
type SecureChannelConfig struct {
// PrimaryName is the DC name (the server principal), e.g. "DC01". Empty sends a NULL
// PrimaryName, which asks the server to use its own name.
PrimaryName string
// ComputerName is the client's NetBIOS computer name (without the trailing '$'), e.g.
// "WORKSTATION".
ComputerName string
// AccountName is the account the channel authenticates as, typically the machine account
// "COMPUTER$" ([MS-NRPC] 3.1.4.1).
AccountName string
// SecureChannelType is the kind of secure channel being set up, e.g.
// WorkstationSecureChannel or ServerSecureChannel.
SecureChannelType msnrpc.NETLOGON_SECURE_CHANNEL_TYPE
// Password is the account cleartext password; ignored when NTHash is set.
Password string
// NTHash is the raw 16-byte NT hash of the account secret (pass-the-hash); nil to derive
// the key material from Password instead.
NTHash []byte
// NegotiateFlags is the client negotiate-flag set; zero selects DefaultNegotiateFlags.
// Whether NegotiateAES is set selects the cipher suite (AES vs legacy strong-key).
NegotiateFlags uint32
// Rand is the source of the 8-byte client challenge; nil selects crypto/rand. It exists
// as a seam for deterministic testing and must be left nil in production.
Rand io.Reader
}