diff options
author | Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> | 2014-05-16 11:46:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-16 17:23:41 -0400 |
commit | 4c14a2fb5d143e4ed94143be2b8c1961b47df9af (patch) | |
tree | 3b10a9a07c5fb699d8797e7e9b96cb30663086e7 /net/mac802154 | |
parent | 03556e4d0dbbbf4af9df76f4a3839c86f6afb015 (diff) |
mac802154: add llsec decryption method
Signed-off-by: Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mac802154')
-rw-r--r-- | net/mac802154/llsec.c | 247 | ||||
-rw-r--r-- | net/mac802154/llsec.h | 1 |
2 files changed, 248 insertions, 0 deletions
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 2a4b68e2a934..392653b1b5a3 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/bug.h> | 18 | #include <linux/bug.h> |
19 | #include <linux/completion.h> | 19 | #include <linux/completion.h> |
20 | #include <net/ieee802154.h> | 20 | #include <net/ieee802154.h> |
21 | #include <crypto/algapi.h> | ||
21 | 22 | ||
22 | #include "mac802154.h" | 23 | #include "mac802154.h" |
23 | #include "llsec.h" | 24 | #include "llsec.h" |
@@ -780,3 +781,249 @@ fail: | |||
780 | rcu_read_unlock(); | 781 | rcu_read_unlock(); |
781 | return rc; | 782 | return rc; |
782 | } | 783 | } |
784 | |||
785 | |||
786 | |||
787 | static struct mac802154_llsec_device* | ||
788 | llsec_lookup_dev(struct mac802154_llsec *sec, | ||
789 | const struct ieee802154_addr *addr) | ||
790 | { | ||
791 | struct ieee802154_addr devaddr = *addr; | ||
792 | struct mac802154_llsec_device *dev = NULL; | ||
793 | |||
794 | if (devaddr.mode == IEEE802154_ADDR_NONE && | ||
795 | llsec_recover_addr(sec, &devaddr) < 0) | ||
796 | return NULL; | ||
797 | |||
798 | if (devaddr.mode == IEEE802154_ADDR_SHORT) { | ||
799 | u32 key = llsec_dev_hash_short(devaddr.short_addr, | ||
800 | devaddr.pan_id); | ||
801 | |||
802 | hash_for_each_possible_rcu(sec->devices_short, dev, | ||
803 | bucket_s, key) { | ||
804 | if (dev->dev.pan_id == devaddr.pan_id && | ||
805 | dev->dev.short_addr == devaddr.short_addr) | ||
806 | return dev; | ||
807 | } | ||
808 | } else { | ||
809 | u64 key = llsec_dev_hash_long(devaddr.extended_addr); | ||
810 | |||
811 | hash_for_each_possible_rcu(sec->devices_hw, dev, | ||
812 | bucket_hw, key) { | ||
813 | if (dev->dev.hwaddr == devaddr.extended_addr) | ||
814 | return dev; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | return NULL; | ||
819 | } | ||
820 | |||
821 | static int | ||
822 | llsec_lookup_seclevel(const struct mac802154_llsec *sec, | ||
823 | u8 frame_type, u8 cmd_frame_id, | ||
824 | struct ieee802154_llsec_seclevel *rlevel) | ||
825 | { | ||
826 | struct ieee802154_llsec_seclevel *level; | ||
827 | |||
828 | list_for_each_entry_rcu(level, &sec->table.security_levels, list) { | ||
829 | if (level->frame_type == frame_type && | ||
830 | (frame_type != IEEE802154_FC_TYPE_MAC_CMD || | ||
831 | level->cmd_frame_id == cmd_frame_id)) { | ||
832 | *rlevel = *level; | ||
833 | return 0; | ||
834 | } | ||
835 | } | ||
836 | |||
837 | return -EINVAL; | ||
838 | } | ||
839 | |||
840 | static int | ||
841 | llsec_do_decrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec, | ||
842 | const struct ieee802154_hdr *hdr, | ||
843 | struct mac802154_llsec_key *key, __le64 dev_addr) | ||
844 | { | ||
845 | u8 iv[16]; | ||
846 | unsigned char *data; | ||
847 | int datalen; | ||
848 | struct scatterlist src; | ||
849 | struct blkcipher_desc req = { | ||
850 | .tfm = key->tfm0, | ||
851 | .info = iv, | ||
852 | .flags = 0, | ||
853 | }; | ||
854 | |||
855 | llsec_geniv(iv, dev_addr, &hdr->sec); | ||
856 | data = skb_mac_header(skb) + skb->mac_len; | ||
857 | datalen = skb_tail_pointer(skb) - data; | ||
858 | |||
859 | sg_init_one(&src, data, datalen); | ||
860 | |||
861 | return crypto_blkcipher_decrypt_iv(&req, &src, &src, datalen); | ||
862 | } | ||
863 | |||
864 | static int | ||
865 | llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, | ||
866 | const struct ieee802154_hdr *hdr, | ||
867 | struct mac802154_llsec_key *key, __le64 dev_addr) | ||
868 | { | ||
869 | u8 iv[16]; | ||
870 | unsigned char *data; | ||
871 | int authlen, datalen, assoclen, rc; | ||
872 | struct scatterlist src, assoc[2]; | ||
873 | struct aead_request *req; | ||
874 | |||
875 | authlen = ieee802154_sechdr_authtag_len(&hdr->sec); | ||
876 | llsec_geniv(iv, dev_addr, &hdr->sec); | ||
877 | |||
878 | req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC); | ||
879 | if (!req) | ||
880 | return -ENOMEM; | ||
881 | |||
882 | sg_init_table(assoc, 2); | ||
883 | sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len); | ||
884 | assoclen = skb->mac_len; | ||
885 | |||
886 | data = skb_mac_header(skb) + skb->mac_len; | ||
887 | datalen = skb_tail_pointer(skb) - data; | ||
888 | |||
889 | if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) { | ||
890 | sg_set_buf(&assoc[1], data, 0); | ||
891 | } else { | ||
892 | sg_set_buf(&assoc[1], data, datalen - authlen); | ||
893 | assoclen += datalen - authlen; | ||
894 | data += datalen - authlen; | ||
895 | datalen = authlen; | ||
896 | } | ||
897 | |||
898 | sg_init_one(&src, data, datalen); | ||
899 | |||
900 | aead_request_set_callback(req, 0, NULL, NULL); | ||
901 | aead_request_set_assoc(req, assoc, assoclen); | ||
902 | aead_request_set_crypt(req, &src, &src, datalen, iv); | ||
903 | |||
904 | rc = crypto_aead_decrypt(req); | ||
905 | |||
906 | kfree(req); | ||
907 | skb_trim(skb, skb->len - authlen); | ||
908 | |||
909 | return rc; | ||
910 | } | ||
911 | |||
912 | static int | ||
913 | llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, | ||
914 | const struct ieee802154_hdr *hdr, | ||
915 | struct mac802154_llsec_key *key, __le64 dev_addr) | ||
916 | { | ||
917 | if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) | ||
918 | return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr); | ||
919 | else | ||
920 | return llsec_do_decrypt_auth(skb, sec, hdr, key, dev_addr); | ||
921 | } | ||
922 | |||
923 | static int | ||
924 | llsec_update_devkey_info(struct mac802154_llsec_device *dev, | ||
925 | const struct ieee802154_llsec_key_id *in_key, | ||
926 | u32 frame_counter) | ||
927 | { | ||
928 | struct mac802154_llsec_device_key *devkey = NULL; | ||
929 | |||
930 | if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RESTRICT) { | ||
931 | devkey = llsec_devkey_find(dev, in_key); | ||
932 | if (!devkey) | ||
933 | return -ENOENT; | ||
934 | } | ||
935 | |||
936 | spin_lock_bh(&dev->lock); | ||
937 | |||
938 | if ((!devkey && frame_counter < dev->dev.frame_counter) || | ||
939 | (devkey && frame_counter < devkey->devkey.frame_counter)) { | ||
940 | spin_unlock_bh(&dev->lock); | ||
941 | return -EINVAL; | ||
942 | } | ||
943 | |||
944 | if (devkey) | ||
945 | devkey->devkey.frame_counter = frame_counter + 1; | ||
946 | else | ||
947 | dev->dev.frame_counter = frame_counter + 1; | ||
948 | |||
949 | spin_unlock_bh(&dev->lock); | ||
950 | |||
951 | return 0; | ||
952 | } | ||
953 | |||
954 | int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb) | ||
955 | { | ||
956 | struct ieee802154_hdr hdr; | ||
957 | struct mac802154_llsec_key *key; | ||
958 | struct ieee802154_llsec_key_id key_id; | ||
959 | struct mac802154_llsec_device *dev; | ||
960 | struct ieee802154_llsec_seclevel seclevel; | ||
961 | int err; | ||
962 | __le64 dev_addr; | ||
963 | u32 frame_ctr; | ||
964 | |||
965 | if (ieee802154_hdr_peek(skb, &hdr) < 0) | ||
966 | return -EINVAL; | ||
967 | if (!hdr.fc.security_enabled) | ||
968 | return 0; | ||
969 | if (hdr.fc.version == 0) | ||
970 | return -EINVAL; | ||
971 | |||
972 | read_lock_bh(&sec->lock); | ||
973 | if (!sec->params.enabled) { | ||
974 | read_unlock_bh(&sec->lock); | ||
975 | return -EINVAL; | ||
976 | } | ||
977 | read_unlock_bh(&sec->lock); | ||
978 | |||
979 | rcu_read_lock(); | ||
980 | |||
981 | key = llsec_lookup_key(sec, &hdr, &hdr.source, &key_id); | ||
982 | if (!key) { | ||
983 | err = -ENOKEY; | ||
984 | goto fail; | ||
985 | } | ||
986 | |||
987 | dev = llsec_lookup_dev(sec, &hdr.source); | ||
988 | if (!dev) { | ||
989 | err = -EINVAL; | ||
990 | goto fail_dev; | ||
991 | } | ||
992 | |||
993 | if (llsec_lookup_seclevel(sec, hdr.fc.type, 0, &seclevel) < 0) { | ||
994 | err = -EINVAL; | ||
995 | goto fail_dev; | ||
996 | } | ||
997 | |||
998 | if (!(seclevel.sec_levels & BIT(hdr.sec.level)) && | ||
999 | (hdr.sec.level == 0 && seclevel.device_override && | ||
1000 | !dev->dev.seclevel_exempt)) { | ||
1001 | err = -EINVAL; | ||
1002 | goto fail_dev; | ||
1003 | } | ||
1004 | |||
1005 | frame_ctr = le32_to_cpu(hdr.sec.frame_counter); | ||
1006 | |||
1007 | if (frame_ctr == 0xffffffff) { | ||
1008 | err = -EOVERFLOW; | ||
1009 | goto fail_dev; | ||
1010 | } | ||
1011 | |||
1012 | err = llsec_update_devkey_info(dev, &key_id, frame_ctr); | ||
1013 | if (err) | ||
1014 | goto fail_dev; | ||
1015 | |||
1016 | dev_addr = dev->dev.hwaddr; | ||
1017 | |||
1018 | rcu_read_unlock(); | ||
1019 | |||
1020 | err = llsec_do_decrypt(skb, sec, &hdr, key, dev_addr); | ||
1021 | llsec_key_put(key); | ||
1022 | return err; | ||
1023 | |||
1024 | fail_dev: | ||
1025 | llsec_key_put(key); | ||
1026 | fail: | ||
1027 | rcu_read_unlock(); | ||
1028 | return err; | ||
1029 | } | ||
diff --git a/net/mac802154/llsec.h b/net/mac802154/llsec.h index 083055b3b795..950578e1d7be 100644 --- a/net/mac802154/llsec.h +++ b/net/mac802154/llsec.h | |||
@@ -103,5 +103,6 @@ int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, | |||
103 | const struct ieee802154_llsec_seclevel *sl); | 103 | const struct ieee802154_llsec_seclevel *sl); |
104 | 104 | ||
105 | int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb); | 105 | int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb); |
106 | int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb); | ||
106 | 107 | ||
107 | #endif /* MAC802154_LLSEC_H */ | 108 | #endif /* MAC802154_LLSEC_H */ |