diff options
author | Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> | 2014-05-16 11:46:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-16 17:23:40 -0400 |
commit | 03556e4d0dbbbf4af9df76f4a3839c86f6afb015 (patch) | |
tree | b7a24d10f74faa0449cb1f11b30e88fbf67ad18e /net/mac802154/llsec.c | |
parent | 5d637d5aabd85132bd85779677d8acb708e0ed90 (diff) |
mac802154: add llsec encryption 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/llsec.c')
-rw-r--r-- | net/mac802154/llsec.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index a210d1eb65a9..2a4b68e2a934 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c | |||
@@ -527,3 +527,256 @@ int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, | |||
527 | 527 | ||
528 | return 0; | 528 | return 0; |
529 | } | 529 | } |
530 | |||
531 | |||
532 | |||
533 | static int llsec_recover_addr(struct mac802154_llsec *sec, | ||
534 | struct ieee802154_addr *addr) | ||
535 | { | ||
536 | __le16 caddr = sec->params.coord_shortaddr; | ||
537 | addr->pan_id = sec->params.pan_id; | ||
538 | |||
539 | if (caddr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) { | ||
540 | return -EINVAL; | ||
541 | } else if (caddr == cpu_to_le16(IEEE802154_ADDR_UNDEF)) { | ||
542 | addr->extended_addr = sec->params.coord_hwaddr; | ||
543 | addr->mode = IEEE802154_ADDR_LONG; | ||
544 | } else { | ||
545 | addr->short_addr = sec->params.coord_shortaddr; | ||
546 | addr->mode = IEEE802154_ADDR_SHORT; | ||
547 | } | ||
548 | |||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | static struct mac802154_llsec_key* | ||
553 | llsec_lookup_key(struct mac802154_llsec *sec, | ||
554 | const struct ieee802154_hdr *hdr, | ||
555 | const struct ieee802154_addr *addr, | ||
556 | struct ieee802154_llsec_key_id *key_id) | ||
557 | { | ||
558 | struct ieee802154_addr devaddr = *addr; | ||
559 | u8 key_id_mode = hdr->sec.key_id_mode; | ||
560 | struct ieee802154_llsec_key_entry *key_entry; | ||
561 | struct mac802154_llsec_key *key; | ||
562 | |||
563 | if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT && | ||
564 | devaddr.mode == IEEE802154_ADDR_NONE) { | ||
565 | if (hdr->fc.type == IEEE802154_FC_TYPE_BEACON) { | ||
566 | devaddr.extended_addr = sec->params.coord_hwaddr; | ||
567 | devaddr.mode = IEEE802154_ADDR_LONG; | ||
568 | } else if (llsec_recover_addr(sec, &devaddr) < 0) { | ||
569 | return NULL; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | list_for_each_entry_rcu(key_entry, &sec->table.keys, list) { | ||
574 | const struct ieee802154_llsec_key_id *id = &key_entry->id; | ||
575 | |||
576 | if (!(key_entry->key->frame_types & BIT(hdr->fc.type))) | ||
577 | continue; | ||
578 | |||
579 | if (id->mode != key_id_mode) | ||
580 | continue; | ||
581 | |||
582 | if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT) { | ||
583 | if (ieee802154_addr_equal(&devaddr, &id->device_addr)) | ||
584 | goto found; | ||
585 | } else { | ||
586 | if (id->id != hdr->sec.key_id) | ||
587 | continue; | ||
588 | |||
589 | if ((key_id_mode == IEEE802154_SCF_KEY_INDEX) || | ||
590 | (key_id_mode == IEEE802154_SCF_KEY_SHORT_INDEX && | ||
591 | id->short_source == hdr->sec.short_src) || | ||
592 | (key_id_mode == IEEE802154_SCF_KEY_HW_INDEX && | ||
593 | id->extended_source == hdr->sec.extended_src)) | ||
594 | goto found; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | return NULL; | ||
599 | |||
600 | found: | ||
601 | key = container_of(key_entry->key, struct mac802154_llsec_key, key); | ||
602 | if (key_id) | ||
603 | *key_id = key_entry->id; | ||
604 | return llsec_key_get(key); | ||
605 | } | ||
606 | |||
607 | |||
608 | static void llsec_geniv(u8 iv[16], __le64 addr, | ||
609 | const struct ieee802154_sechdr *sec) | ||
610 | { | ||
611 | __be64 addr_bytes = (__force __be64) swab64((__force u64) addr); | ||
612 | __be32 frame_counter = (__force __be32) swab32((__force u32) sec->frame_counter); | ||
613 | |||
614 | iv[0] = 1; /* L' = L - 1 = 1 */ | ||
615 | memcpy(iv + 1, &addr_bytes, sizeof(addr_bytes)); | ||
616 | memcpy(iv + 9, &frame_counter, sizeof(frame_counter)); | ||
617 | iv[13] = sec->level; | ||
618 | iv[14] = 0; | ||
619 | iv[15] = 1; | ||
620 | } | ||
621 | |||
622 | static int | ||
623 | llsec_do_encrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec, | ||
624 | const struct ieee802154_hdr *hdr, | ||
625 | struct mac802154_llsec_key *key) | ||
626 | { | ||
627 | u8 iv[16]; | ||
628 | struct scatterlist src; | ||
629 | struct blkcipher_desc req = { | ||
630 | .tfm = key->tfm0, | ||
631 | .info = iv, | ||
632 | .flags = 0, | ||
633 | }; | ||
634 | |||
635 | llsec_geniv(iv, sec->params.hwaddr, &hdr->sec); | ||
636 | sg_init_one(&src, skb->data, skb->len); | ||
637 | return crypto_blkcipher_encrypt_iv(&req, &src, &src, skb->len); | ||
638 | } | ||
639 | |||
640 | static struct crypto_aead* | ||
641 | llsec_tfm_by_len(struct mac802154_llsec_key *key, int authlen) | ||
642 | { | ||
643 | int i; | ||
644 | |||
645 | for (i = 0; i < ARRAY_SIZE(key->tfm); i++) | ||
646 | if (crypto_aead_authsize(key->tfm[i]) == authlen) | ||
647 | return key->tfm[i]; | ||
648 | |||
649 | BUG(); | ||
650 | } | ||
651 | |||
652 | static int | ||
653 | llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, | ||
654 | const struct ieee802154_hdr *hdr, | ||
655 | struct mac802154_llsec_key *key) | ||
656 | { | ||
657 | u8 iv[16]; | ||
658 | unsigned char *data; | ||
659 | int authlen, assoclen, datalen, rc; | ||
660 | struct scatterlist src, assoc[2], dst[2]; | ||
661 | struct aead_request *req; | ||
662 | |||
663 | authlen = ieee802154_sechdr_authtag_len(&hdr->sec); | ||
664 | llsec_geniv(iv, sec->params.hwaddr, &hdr->sec); | ||
665 | |||
666 | req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC); | ||
667 | if (!req) | ||
668 | return -ENOMEM; | ||
669 | |||
670 | sg_init_table(assoc, 2); | ||
671 | sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len); | ||
672 | assoclen = skb->mac_len; | ||
673 | |||
674 | data = skb_mac_header(skb) + skb->mac_len; | ||
675 | datalen = skb_tail_pointer(skb) - data; | ||
676 | |||
677 | if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) { | ||
678 | sg_set_buf(&assoc[1], data, 0); | ||
679 | } else { | ||
680 | sg_set_buf(&assoc[1], data, datalen); | ||
681 | assoclen += datalen; | ||
682 | datalen = 0; | ||
683 | } | ||
684 | |||
685 | sg_init_one(&src, data, datalen); | ||
686 | |||
687 | sg_init_table(dst, 2); | ||
688 | sg_set_buf(&dst[0], data, datalen); | ||
689 | sg_set_buf(&dst[1], skb_put(skb, authlen), authlen); | ||
690 | |||
691 | aead_request_set_callback(req, 0, NULL, NULL); | ||
692 | aead_request_set_assoc(req, assoc, assoclen); | ||
693 | aead_request_set_crypt(req, &src, dst, datalen, iv); | ||
694 | |||
695 | rc = crypto_aead_encrypt(req); | ||
696 | |||
697 | kfree(req); | ||
698 | |||
699 | return rc; | ||
700 | } | ||
701 | |||
702 | static int llsec_do_encrypt(struct sk_buff *skb, | ||
703 | const struct mac802154_llsec *sec, | ||
704 | const struct ieee802154_hdr *hdr, | ||
705 | struct mac802154_llsec_key *key) | ||
706 | { | ||
707 | if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) | ||
708 | return llsec_do_encrypt_unauth(skb, sec, hdr, key); | ||
709 | else | ||
710 | return llsec_do_encrypt_auth(skb, sec, hdr, key); | ||
711 | } | ||
712 | |||
713 | int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) | ||
714 | { | ||
715 | struct ieee802154_hdr hdr; | ||
716 | int rc, authlen, hlen; | ||
717 | struct mac802154_llsec_key *key; | ||
718 | u32 frame_ctr; | ||
719 | |||
720 | hlen = ieee802154_hdr_pull(skb, &hdr); | ||
721 | |||
722 | if (hlen < 0 || hdr.fc.type != IEEE802154_FC_TYPE_DATA) | ||
723 | return -EINVAL; | ||
724 | |||
725 | if (!hdr.fc.security_enabled || hdr.sec.level == 0) { | ||
726 | skb_push(skb, hlen); | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | authlen = ieee802154_sechdr_authtag_len(&hdr.sec); | ||
731 | |||
732 | if (skb->len + hlen + authlen + IEEE802154_MFR_SIZE > IEEE802154_MTU) | ||
733 | return -EMSGSIZE; | ||
734 | |||
735 | rcu_read_lock(); | ||
736 | |||
737 | read_lock_bh(&sec->lock); | ||
738 | |||
739 | if (!sec->params.enabled) { | ||
740 | rc = -EINVAL; | ||
741 | goto fail_read; | ||
742 | } | ||
743 | |||
744 | key = llsec_lookup_key(sec, &hdr, &hdr.dest, NULL); | ||
745 | if (!key) { | ||
746 | rc = -ENOKEY; | ||
747 | goto fail_read; | ||
748 | } | ||
749 | |||
750 | read_unlock_bh(&sec->lock); | ||
751 | |||
752 | write_lock_bh(&sec->lock); | ||
753 | |||
754 | frame_ctr = be32_to_cpu(sec->params.frame_counter); | ||
755 | hdr.sec.frame_counter = cpu_to_le32(frame_ctr); | ||
756 | if (frame_ctr == 0xFFFFFFFF) { | ||
757 | write_unlock_bh(&sec->lock); | ||
758 | llsec_key_put(key); | ||
759 | rc = -EOVERFLOW; | ||
760 | goto fail; | ||
761 | } | ||
762 | |||
763 | sec->params.frame_counter = cpu_to_be32(frame_ctr + 1); | ||
764 | |||
765 | write_unlock_bh(&sec->lock); | ||
766 | |||
767 | rcu_read_unlock(); | ||
768 | |||
769 | skb->mac_len = ieee802154_hdr_push(skb, &hdr); | ||
770 | skb_reset_mac_header(skb); | ||
771 | |||
772 | rc = llsec_do_encrypt(skb, sec, &hdr, key); | ||
773 | llsec_key_put(key); | ||
774 | |||
775 | return rc < 0 ? rc : 0; | ||
776 | |||
777 | fail_read: | ||
778 | read_unlock(&sec->lock); | ||
779 | fail: | ||
780 | rcu_read_unlock(); | ||
781 | return rc; | ||
782 | } | ||