Documentation
¶
Overview ¶
Package crypto implements the IKEv2 cryptographic primitives and proposal negotiation.
Index ¶
- Variables
- func ComputeIntegrity(id IntegrityID, key, data []byte) ([]byte, error)
- func DecryptAESCBCRaw(key, data []byte) ([]byte, error)
- func DecryptIKEAEAD(keyWithSalt, data, aad []byte) ([]byte, error)
- func DeriveRekeyedSKEYSEED(prfID PRFID, skDOld, newSharedSecret, ni, nr []byte) ([]byte, error)
- func DeriveSKEYSEED(prfID PRFID, ni, nr, sharedSecret []byte) ([]byte, error)
- func PRF(id PRFID, key, data []byte) ([]byte, error)
- func PRFPlus(id PRFID, key, seed []byte, length int) ([]byte, error)
- func SupportedDHGroupIDs() []uint8
- func SupportedEncryptionNames() []string
- func SupportedIntegrityNames() []string
- func SupportedPRFNames() []string
- func TransformTypeUnderstoodESP(tt TransformType) bool
- func TransformTypeUnderstoodIKE(tt TransformType) bool
- func VerifyIntegrity(id IntegrityID, key, data, expected []byte) error
- type ChildSAKeys
- type DHExchange
- type DHGroupID
- type DHGroupTransform
- type ESPProposal
- type EncryptionID
- type EncryptionTransform
- type IKEProposal
- type IntegrityID
- type IntegrityTransform
- type PRFID
- type PRFTransform
- type SKKeys
- type TransformType
Constants ¶
This section is empty.
Variables ¶
var ( ErrDecryptionFailed = errors.New("decryption failed: authentication tag mismatch") ErrInvalidKeyLength = errors.New("invalid key length") ErrIntegrityFailed = errors.New("integrity verification failed") )
var ( ErrInvalidPublicKey = errors.New("invalid DH public key") ErrUnsupportedGroup = errors.New("unsupported DH group") // ErrPublicKeyLength reports a peer Diffie-Hellman value whose length is not the // length of the group modulus. RFC 7296 Section 3.4 makes that pad an obligation of // the sender. A value of another length therefore names another group, or a peer // that did not pad. Both cases end in a secret the two sides do not share, so the // exponentiation never runs. It wraps ErrInvalidPublicKey. A value of the wrong // length is an invalid public key, so a caller that tests for the general case // still matches this one. ErrPublicKeyLength = fmt.Errorf("%w: length does not match the group modulus", ErrInvalidPublicKey) // ErrClearedExchange reports a shared-secret computation attempted after the // exchange forgot its private value. // // RFC 7296 Section 2.12 requires a closed connection to forget the information that // could recompute its keys, and SA.forgetKeys clears exactly this. A caller reaching // SharedSecret afterwards would exponentiate with no exponent, so the answer is an // error rather than a value neither side shares. ErrClearedExchange = errors.New("diffie-hellman exchange has been cleared") )
var ( // ErrProposalIncomplete reports a proposal that omits a mandatory transform // type (RFC 7296 Section 3.3.6). ErrProposalIncomplete = fmt.Errorf("%w: proposal omits a mandatory transform type", ErrNoProposalChosen) // ErrTransformUnspecified reports a transform identifier this implementation // has no specification for (RFC 7296 Sections 3.3.6 and 3.14). ErrTransformUnspecified = fmt.Errorf("%w: transform has no specification", ErrNoProposalChosen) // ErrTransformTypeNotUnderstood reports a proposal that carries a Transform Type the // protocol does not use (RFC 7296 Sections 3.3.3 and 3.3.6). ErrTransformTypeNotUnderstood = fmt.Errorf("%w: proposal carries a transform type this protocol does not use", ErrNoProposalChosen) // ErrKeyLengthMissing reports a transform that requires a Key Length attribute // and carries none (RFC 7296 Section 3.3.5). ErrKeyLengthMissing = fmt.Errorf("%w: transform requires a key length attribute", ErrNoProposalChosen) // ErrDHGroupNone reports a Diffie-Hellman group of NONE offered for an IKE SA // (RFC 7296 Section 2.18). ErrDHGroupNone = fmt.Errorf("%w: Diffie-Hellman group NONE is not allowed for an IKE SA", ErrNoProposalChosen) // ErrPRFTooWeak reports a PRF whose output is below the floor RFC 7296 // Section 5 sets. ErrPRFTooWeak = fmt.Errorf("%w: PRF output is below 128 bits", ErrNoProposalChosen) )
var ErrNoProposalChosen = errNoProposalChosen{}
ErrNoProposalChosen is the refusal the responder reports to the peer. It becomes the NO_PROPOSAL_CHOSEN notification. Every reason below wraps it. A caller that asks only whether negotiation failed keeps working. A caller that wants the reason reads it with errors.Is.
var ErrUnsupportedAlgorithm = errors.New("unsupported algorithm")
Functions ¶
func ComputeIntegrity ¶
func ComputeIntegrity(id IntegrityID, key, data []byte) ([]byte, error)
ComputeIntegrity computes a truncated HMAC for the given data.
func DecryptAESCBCRaw ¶
DecryptAESCBCRaw decrypts without unpadding (caller handles IKEv2 padding). Input is iv(blockSize) || ciphertext.
func DecryptIKEAEAD ¶
DecryptIKEAEAD decrypts using the IKEv2 AEAD construction (RFC 5282). keyWithSalt is the AEAD key material: AES key || 4-byte salt. data is IV(8) || ciphertext || tag(16) from the wire.
func DeriveRekeyedSKEYSEED ¶
DeriveRekeyedSKEYSEED computes the seed key for a rekeyed IKE SA. RFC 7296 Section 2.18: SKEYSEED = prf(SK_d_old, g^ir_new | Ni | Nr).
func DeriveSKEYSEED ¶
DeriveSKEYSEED computes the initial IKE SA seed key. RFC 7296 Section 2.14: SKEYSEED = prf(Ni | Nr, g^ir).
func PRFPlus ¶
PRFPlus implements RFC 7296 Section 2.13 prf+ key expansion. T1 = prf(K, S | 0x01) T2 = prf(K, T1 | S | 0x02) T3 = prf(K, T2 | S | 0x03) ... prf+(K, S) = T1 | T2 | T3 | ...
func SupportedDHGroupIDs ¶
func SupportedDHGroupIDs() []uint8
SupportedDHGroupIDs lists every Diffie-Hellman group this build implements, in ascending order. RFC 7296 Section 3.3.2 assigns Transform Type 4 a far wider number space than any build carries. The config parser therefore names this list in the error it returns for a group it refuses, so the two can never disagree (ai/rules/evidence.md).
func SupportedEncryptionNames ¶
func SupportedEncryptionNames() []string
SupportedEncryptionNames lists every encryption algorithm this build implements, in sorted order. The config parser names the list in the error it returns for an algorithm it refuses, so the two can never disagree (ai/rules/evidence.md).
func SupportedIntegrityNames ¶
func SupportedIntegrityNames() []string
SupportedIntegrityNames lists every integrity algorithm this build implements, in sorted order. SupportedEncryptionNames gives the reason it is derived.
func SupportedPRFNames ¶
func SupportedPRFNames() []string
SupportedPRFNames lists every PRF this build implements, in sorted order.
func TransformTypeUnderstoodESP ¶
func TransformTypeUnderstoodESP(tt TransformType) bool
TransformTypeUnderstoodESP reports whether an ESP proposal uses this transform type, on the same terms as TransformTypeUnderstoodIKE.
func TransformTypeUnderstoodIKE ¶
func TransformTypeUnderstoodIKE(tt TransformType) bool
TransformTypeUnderstoodIKE reports whether an IKE SA proposal uses this transform type. The reader of a wire proposal calls it for every transform it meets. A type that reads false makes the proposal unacceptable under RFC 7296 Section 3.3.6, and the reader records it in UnknownTransformType.
func VerifyIntegrity ¶
func VerifyIntegrity(id IntegrityID, key, data, expected []byte) error
VerifyIntegrity checks a truncated HMAC using constant-time comparison.
Types ¶
type ChildSAKeys ¶
ChildSAKeys holds ESP keying material for one direction.
func DeriveChildSAKeys ¶
func DeriveChildSAKeys(prfID PRFID, skD, ni, nr []byte, enc EncryptionTransform, integ IntegrityTransform) (*ChildSAKeys, error)
DeriveChildSAKeys derives ESP keys from SK_d and nonces. RFC 7296 Section 2.17: KEYMAT = prf+(SK_d, Ni | Nr).
An AEAD cipher takes four octets of salt beyond its key (RFC 4106 Section 8.1), so encKeyMaterialLen decides the length of each of the four keys below.
func DeriveChildSAKeysPFS ¶
func DeriveChildSAKeysPFS(prfID PRFID, skD, dhSharedSecret, ni, nr []byte, enc EncryptionTransform, integ IntegrityTransform) (*ChildSAKeys, error)
DeriveChildSAKeysPFS derives ESP keys with Perfect Forward Secrecy. RFC 7296 Section 2.17: KEYMAT = prf+(SK_d, g^ir | Ni | Nr).
The AEAD salt rule of DeriveChildSAKeys applies here without change.
func (*ChildSAKeys) Clear ¶
func (k *ChildSAKeys) Clear()
type DHExchange ¶
type DHExchange struct {
GroupID DHGroupID
PublicKey []byte
// contains filtered or unexported fields
}
func NewDHExchange ¶
func NewDHExchange(groupID DHGroupID) (*DHExchange, error)
func (*DHExchange) Clear ¶
func (ex *DHExchange) Clear()
func (*DHExchange) HasPrivate ¶
func (ex *DHExchange) HasPrivate() bool
HasPrivate reports whether this exchange still holds its private value.
RFC 7296 Section 2.12 requires a closed connection to forget "any information that could be used to recompute those keys", and the private exponent is that information: with the peer's public value it recomputes g^ir, and SKEYSEED with it. The private fields are unexported, so this is how a caller outside the package confirms Clear ran.
func (*DHExchange) SharedSecret ¶
func (ex *DHExchange) SharedSecret(remotePublic []byte) ([]byte, error)
type DHGroupTransform ¶
type DHGroupTransform struct {
ID DHGroupID
}
func LookupDHGroup ¶
func LookupDHGroup(id uint8) (DHGroupTransform, error)
type ESPProposal ¶
type ESPProposal struct {
Number uint16
Encryption EncryptionTransform
Integrity IntegrityTransform
// UnknownTransformType holds the first Transform Type the peer offered that ESP
// does not use. The IKE field of the same name records it on the same terms.
UnknownTransformType TransformType
}
ESPProposal represents a single ESP/Child SA crypto proposal.
func NegotiateESP ¶
func NegotiateESP(accepted, sent []ESPProposal) (ESPProposal, error)
NegotiateESP checks the Child SA offer a responder accepted against the proposals this side sent. It reads the same per-transform terms as VerifyAcceptedIKE. It is the INITIATOR path, and the initiator check is its only caller. RFC 7296 Section 3.3.6 obliges the initiator to check the accepted offer against its own proposals. The Key Length attribute is therefore read under keyLengthExact.
The responder's own Child SA selection does not come through here. It reads the wire proposals directly, in matchOfferedESPProposal (ike/engine/responder.go), because it also needs the Proposal Num that RFC 7296 Section 3.3.1 makes the response echo. That path compares the key length for equality too.
The accepted proposal is returned with the peer's own attributes, which RFC 7296 Section 3.3.6 requires to be returned unmodified.
type EncryptionID ¶
type EncryptionID uint16
RFC 7296 Section 3.3.3: Encryption Algorithm Transform IDs.
const ( ENCR_AES_CBC EncryptionID = 12 ENCR_AES_GCM_16 EncryptionID = 20 )
func (EncryptionID) IsAEAD ¶
func (id EncryptionID) IsAEAD() bool
IsAEAD reports whether this encryption transform combines integrity with encryption. RFC 7296 Section 3.3 makes an integrity transform of NONE the correct value for such a cipher.
The verdict comes from the Transform ID, which is the identity the peer put on the wire. A caller that holds an EncryptionTransform asks this method on the ID rather than read the IsAEAD field. The field is a cached view. A construction site can leave it at its zero value, and that false value reads as a valid "not AEAD" answer (ai/rules/evidence.md). The ID cannot lie in that way.
Membership in aeadSaltBytes IS the AEAD property. A miss means the cipher is not AEAD. A hit gives that cipher's own salt. Neither answer can be a zero value that reads as valid.
func (EncryptionID) String ¶
func (id EncryptionID) String() string
type EncryptionTransform ¶
type EncryptionTransform struct {
ID EncryptionID
KeyLength uint16 // in bits
IsAEAD bool
}
EncryptionTransform names one encryption algorithm and the key it takes.
IsAEAD caches the verdict of EncryptionID.IsAEAD for the ID this transform holds. Build a transform with NewEncryptionTransform to keep the two in agreement. Any decision that must be correct reads ID.IsAEAD, never the field.
func LookupEncryption ¶
func LookupEncryption(name string) (EncryptionTransform, error)
func NewEncryptionTransform ¶
func NewEncryptionTransform(id EncryptionID, keyLengthBits uint16) EncryptionTransform
NewEncryptionTransform builds a transform whose IsAEAD field agrees with its ID. Every site that learns an encryption ID at run time uses it, so no site has to remember the AEAD property on its own.
type IKEProposal ¶
type IKEProposal struct {
Number uint16
Encryption EncryptionTransform
PRF PRFTransform
Integrity IntegrityTransform
DHGroup DHGroupTransform
// PolicyKeyLength holds the encryption key length local policy configured, when the
// responder accepted a longer key under RFC 7296 Section 3.3.5. It is zero when the
// accepted key length is the configured one. The caller reports every non-zero value,
// because the running key then differs from the configured key.
PolicyKeyLength uint16
// UnknownTransformType holds the first Transform Type the peer offered that IKE does
// not use. The reader of a wire proposal sets it from TransformTypeUnderstoodIKE. RFC
// 7296 Section 3.3.6 makes such a proposal unacceptable, and negotiation refuses it.
// Zero means every type the peer offered is one IKE uses, because RFC 7296
// Section 3.3.2 reserves the type zero.
UnknownTransformType TransformType
}
IKEProposal represents a single IKE SA crypto proposal.
func NegotiateIKE ¶
func NegotiateIKE(remote, local []IKEProposal) (IKEProposal, error)
NegotiateIKE selects one complete set of IKE SA parameters from the offers a peer sends. It is the RESPONDER path. RFC 7296 Section 3.3.6 requires the responder to select a single complete set of parameters, or to reject every offer.
The responder reads the Key Length attribute under keyLengthAtLeast, which RFC 7296 Section 3.3.5 allows: implementers SHOULD accept values that they deem to supply greater security. An accepted key longer than the configured one sets PolicyKeyLength on the result, and the caller reports it.
The initiator MUST NOT use this function. Its own obligation is stricter, and VerifyAcceptedIKE holds it.
func VerifyAcceptedIKE ¶
func VerifyAcceptedIKE(accepted, sent []IKEProposal) (IKEProposal, error)
VerifyAcceptedIKE checks the IKE offer a responder accepted against the proposals this side sent. It is the INITIATOR path. RFC 7296 Section 3.3.6: "The initiator of an exchange MUST check that the accepted offer is consistent with one of its proposals, and if not MUST terminate the exchange".
The check reads the Key Length attribute under keyLengthExact. RFC 7296 Section 3.3.5 states that the attribute returns unchanged, and that an initiator which accepts several key lengths sends one transform for each. A key length this side never sent is therefore an inconsistent answer, above the configured length as much as below it.
type IntegrityID ¶
type IntegrityID uint16
RFC 7296 Section 3.3.5: Integrity Algorithm Transform IDs.
const ( AUTH_NONE IntegrityID = 0 AUTH_HMAC_SHA2_256_128 IntegrityID = 12 AUTH_HMAC_SHA2_384_192 IntegrityID = 13 AUTH_HMAC_SHA2_512_256 IntegrityID = 14 )
func (IntegrityID) String ¶
func (id IntegrityID) String() string
type IntegrityTransform ¶
type IntegrityTransform struct {
ID IntegrityID
KeyLength uint16 // in bytes
TruncatedLength uint16 // ICV length in bytes
}
func LookupIntegrity ¶
func LookupIntegrity(name string) (IntegrityTransform, error)
type PRFTransform ¶
func LookupPRF ¶
func LookupPRF(name string) (PRFTransform, error)
type SKKeys ¶
type SKKeys struct {
SK_d []byte // Key derivation key for Child SA KEYMAT.
SK_ai []byte // Initiator integrity key.
SK_ar []byte // Responder integrity key.
SK_ei []byte // Initiator encryption key.
SK_er []byte // Responder encryption key.
SK_pi []byte // Initiator AUTH payload key.
SK_pr []byte // Responder AUTH payload key.
}
SKKeys holds the IKE SA key hierarchy derived from SKEYSEED.
func DeriveSKKeys ¶
func DeriveSKKeys(prfID PRFID, skeyseed, ni, nr, spiI, spiR []byte, enc EncryptionTransform, integ IntegrityTransform) (*SKKeys, error)
DeriveSKKeys expands SKEYSEED into the full SK_* key hierarchy. RFC 7296 Section 2.14: {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr} =
prf+(SKEYSEED, Ni | Nr | SPIi | SPIr).
type TransformType ¶
type TransformType uint8
RFC 7296 Section 3.3.2: Transform Type Values.
const ( TransformTypeENCR TransformType = 1 TransformTypePRF TransformType = 2 TransformTypeINTEG TransformType = 3 TransformTypeDH TransformType = 4 TransformTypeESN TransformType = 5 )