Hi! I refactored the simulator to change its GUI and took the time to extend it a bit to support OTAA. Besides a couple of issues with frame counters that I’ll take care of before merging, it works fine for 1.0 but I’m having some troubles with 1.1. In both cases a join looks like this:
//Join sends a join request for a given device (OTAA) and rxInfo.
func (d *Device) Join(client MQTT.Client, gwMac string, rxInfo RxInfo) error {
devNonceKey := fmt.Sprintf("dev-nonce-%s", d.DevEUI[:])
var devNonce uint16
sdn, err := redisClient.Get(devNonceKey).Result()
if err == nil {
dn, err := strconv.Atoi(sdn)
if err == nil {
devNonce = uint16(dn + 1)
}
}
redisClient.Set(devNonceKey, devNonce, 0)
d.DevNonce = lorawan.DevNonce(devNonce)
joinPhy := lorawan.PHYPayload{
MHDR: lorawan.MHDR{
MType: lorawan.JoinRequest,
Major: lorawan.LoRaWANR1,
},
MACPayload: &lorawan.JoinRequestPayload{
JoinEUI: d.AppEUI,
DevEUI: d.DevEUI,
DevNonce: d.DevNonce,
},
}
if err := joinPhy.SetUplinkJoinMIC(d.NwkKey); err != nil {
return err
}
joinStr, err := joinPhy.MarshalText()
if err != nil {
return err
}
message := &Message{
PhyPayload: string(joinStr),
RxInfo: &rxInfo,
}
pErr := publish(client, "gateway/"+rxInfo.Mac+"/rx", message)
return pErr
}
Now, the next function processes the join response and, as mentioned, it works fine when setting to say 1.0.2 the device’s mac version at loraserver:
func (d *Device) processJoinResponse(phy lorawan.PHYPayload, payload []byte, mv lorawan.MACVersion) (string, error) {
log.Infoln("processing join response")
err := phy.DecryptJoinAcceptPayload(d.NwkKey)
if err != nil {
log.Errorf("can't decrypt join accept: %s", err)
}
log.Infof("unmarshaled: %s", payload)
ok, err := phy.ValidateDownlinkJoinMIC(lorawan.JoinRequestType, d.DevEUI, d.DevNonce, d.NwkKey)
if err != nil {
log.Error("failed at join mic function")
return "", err
}
if !ok {
return "", errors.New("join invalid mic")
}
if err := phy.DecryptFOpts(d.NwkKey); err != nil {
log.Error("failed at opts decryption")
return "", err
}
phyJSON, err := phy.MarshalJSON()
if err != nil {
log.Error("failed at json marshal")
return "", err
}
jap := phy.MACPayload.(*lorawan.JoinAcceptPayload)
log.Infof("join accept payload: %+v", jap)
//Check that JoinNonce is greater than the one already stored.
joinNonceKey := fmt.Sprintf("join-nonce-%s", d.DevEUI[:])
var joinNonce lorawan.JoinNonce
sjn, err := redisClient.Get("join-nonce").Result()
if err == nil {
jn, err := strconv.Atoi(sjn)
if err == nil {
joinNonce = lorawan.JoinNonce(jn + 1)
}
}
if jap.JoinNonce <= joinNonce {
return "", errors.New("got lower or equal JoinNonce from server")
}
redisClient.Set(joinNonceKey, joinNonce, 0)
d.FNwkSIntKey, err = getFNwkSIntKey(jap.DLSettings.OptNeg, d.NwkKey, jap.HomeNetID, d.DevEUI, jap.JoinNonce, d.DevNonce)
if d.MACVersion == 0 {
d.NwkSEncKey = d.FNwkSIntKey
d.SNwkSIntKey = d.FNwkSIntKey
} else {
d.NwkSEncKey, err = getNwkSEncKey(jap.DLSettings.OptNeg, d.NwkKey, jap.HomeNetID, d.DevEUI, jap.JoinNonce, d.DevNonce)
d.SNwkSIntKey, err = getSNwkSIntKey(jap.DLSettings.OptNeg, d.NwkKey, jap.HomeNetID, d.DevEUI, jap.JoinNonce, d.DevNonce)
}
if jap.DLSettings.OptNeg {
d.AppSKey, err = getAppSKey(jap.DLSettings.OptNeg, d.AppKey, jap.HomeNetID, d.DevEUI, jap.JoinNonce, d.DevNonce)
} else {
d.AppSKey, err = getAppSKey(jap.DLSettings.OptNeg, d.NwkKey, jap.HomeNetID, d.DevEUI, jap.JoinNonce, d.DevNonce)
}
d.DevAddr = jap.DevAddr
return string(phyJSON), nil
}
For 1.1., this fails at ValidateDownlinkJoinMIC
, which returns false, nil
, indicating the MIC is incorrect. Accordingly, if I just remove the validation, devAddr
is correct but all keys are wrong and uplinks don’t work, which of course is expected.
Anyway, if anyone knows what I’m doing wrong when processing the join accept (or maybe it’s 1.1’s join that should be different), it’d be of great help.