forked from holgermetschulat/radius
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add functions for MPPE handshakes (RFC 3079)
This patch adds support for adding MPPE send/recv keys that are attached to MSCHAPv2 response packets, as described by RFC 3079. The results from MakeKey() can be used as input for NewTunnelPassword(). Code is tested with examples from section 3.5 of the RFC.
- Loading branch information
Showing
2 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package rfc3079 | ||
|
||
import ( | ||
"crypto/sha1" | ||
"errors" | ||
|
||
"layeh.com/radius/rfc2759" | ||
) | ||
|
||
// KeyLength is the length of keys involved with the functions below | ||
type KeyLength uint | ||
|
||
const ( | ||
// KeyLength40Bit - 40-bit | ||
KeyLength40Bit = KeyLength(8) | ||
// KeyLength128Bit - 128-bit | ||
KeyLength128Bit = KeyLength(16) | ||
) | ||
|
||
var ( | ||
shaPad1 = []byte{ | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
} | ||
|
||
shaPad2 = []byte{ | ||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, | ||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, | ||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, | ||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, | ||
} | ||
|
||
magic1 = []byte{ | ||
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, | ||
0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, | ||
0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79, | ||
} | ||
|
||
magic2 = []byte{ | ||
0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, | ||
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, | ||
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, | ||
0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, | ||
0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, | ||
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, | ||
0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, | ||
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, | ||
0x6b, 0x65, 0x79, 0x2e, | ||
} | ||
|
||
magic3 = []byte{ | ||
0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, | ||
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, | ||
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, | ||
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, | ||
0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, | ||
0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, | ||
0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, | ||
0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, | ||
0x6b, 0x65, 0x79, 0x2e, | ||
} | ||
) | ||
|
||
// GetMasterKey - rfc3079, 3.4 | ||
func GetMasterKey(passwordHashHash, ntResponse []byte) []byte { | ||
sha := sha1.New() | ||
sha.Write(passwordHashHash) | ||
sha.Write(ntResponse) | ||
sha.Write(magic1) | ||
digest := sha.Sum(nil) | ||
|
||
return digest[:16] | ||
} | ||
|
||
// GetAsymmetricStartKey - rfc3079, 3.4 | ||
func GetAsymmetricStartKey(masterKey []byte, sessionKeyLength KeyLength, isSend bool) ([]byte, error) { | ||
if len(masterKey) != 16 { | ||
return []byte{}, errors.New("masterKey must be 16 bytes long") | ||
} | ||
|
||
s := []byte{} | ||
|
||
if isSend { | ||
s = magic3 | ||
} else { | ||
s = magic2 | ||
} | ||
|
||
sha := sha1.New() | ||
sha.Write(masterKey) | ||
sha.Write(shaPad1) | ||
sha.Write(s) | ||
sha.Write(shaPad2) | ||
digest := sha.Sum(nil) | ||
|
||
return digest[:sessionKeyLength], nil | ||
} | ||
|
||
// MakeKey - rfc2548, 2.4.2 | ||
func MakeKey(ntResponse []byte, password string, isSend bool) ([]byte, error) { | ||
if len(ntResponse) != 24 { | ||
return []byte{}, errors.New("ntresponse must be 24 bytes in size") | ||
} | ||
|
||
ucs2Password, err := rfc2759.ToUTF16(password) | ||
if err != nil { | ||
return []byte{}, err | ||
} | ||
|
||
passwordHash := rfc2759.HashPassword(ucs2Password) | ||
passwordHashHash := rfc2759.HashPassword(passwordHash) | ||
masterKey := GetMasterKey(passwordHashHash, ntResponse) | ||
|
||
return GetAsymmetricStartKey(masterKey, KeyLength128Bit, isSend) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package rfc3079 | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestGetMasterKey(t *testing.T) { | ||
type args struct { | ||
passwordHashHash []byte | ||
ntResponse []byte | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want []byte | ||
}{ | ||
{ | ||
name: "rfc3079, 3.5.1", | ||
args: args{ | ||
passwordHashHash: []byte{ | ||
0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F, | ||
}, | ||
ntResponse: []byte{ | ||
0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, 0x42, 0x33, | ||
0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF, | ||
}, | ||
}, | ||
want: []byte{ | ||
0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := GetMasterKey(tt.args.passwordHashHash, tt.args.ntResponse); !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("GetMasterKey() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestGetAsymmetricStartKey(t *testing.T) { | ||
type args struct { | ||
masterKey []byte | ||
sessionKeyLength KeyLength | ||
isSend bool | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want []byte | ||
}{ | ||
{ | ||
name: "40-bit, rfc3079, 3.5.1", | ||
args: args{ | ||
masterKey: []byte{ | ||
0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31, | ||
}, | ||
sessionKeyLength: KeyLength40Bit, | ||
isSend: true, | ||
}, | ||
want: []byte{ | ||
0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, | ||
}, | ||
}, | ||
{ | ||
name: "128-bit, rfc3079, 3.5.3", | ||
args: args{ | ||
masterKey: []byte{ | ||
0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31, | ||
}, | ||
sessionKeyLength: 16, | ||
isSend: true, | ||
}, | ||
want: []byte{ | ||
0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := GetAsymmetricStartKey(tt.args.masterKey, tt.args.sessionKeyLength, tt.args.isSend) | ||
if err != nil { | ||
t.Errorf("err = %v", err) | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("GetAsymmetricStartKey() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |