diff options
Diffstat (limited to 'TLSA.hs')
-rw-r--r-- | TLSA.hs | 133 |
1 files changed, 133 insertions, 0 deletions
@@ -0,0 +1,133 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | module TLSA | ||
3 | ( TLSA(..) | ||
4 | , CertUsage(..) | ||
5 | , Selector(..) | ||
6 | , MatchingType(..) | ||
7 | , fromByteString | ||
8 | , toByteString | ||
9 | , match | ||
10 | ) where | ||
11 | |||
12 | import qualified Data.ByteString as BS | ||
13 | import qualified Data.ByteString.Lazy as L | ||
14 | import qualified Crypto.Hash.SHA256 as SHA256 | ||
15 | import qualified Crypto.Hash.SHA512 as SHA512 | ||
16 | import Data.X509 ( certPubKey, Certificate ) | ||
17 | import Data.ASN1.Types ( toASN1 ) | ||
18 | import Data.ASN1.Encoding ( encodeASN1 ) | ||
19 | import Data.ASN1.BinaryEncoding | ||
20 | import Control.Applicative | ||
21 | import Data.Word | ||
22 | import Data.Maybe | ||
23 | import Data.Monoid | ||
24 | |||
25 | {- INLINE fromWord8 #-} | ||
26 | fromWord8 :: Enum a => Word8 -> a | ||
27 | fromWord8 = toEnum . fromEnum | ||
28 | |||
29 | {- INLINE toWord8 -} | ||
30 | toWord8 :: Enum a => a -> Word8 | ||
31 | toWord8 = toEnum . fromEnum | ||
32 | |||
33 | data CertUsage | ||
34 | = CAConstraint | ||
35 | | ServiceCertificateConstraint | ||
36 | | TrustAnchorAssertion | ||
37 | | DomainIssued | ||
38 | | CertUsage Word8 | ||
39 | deriving (Eq, Ord, Show, Read) | ||
40 | |||
41 | instance Enum CertUsage where | ||
42 | fromEnum CAConstraint = 0 | ||
43 | fromEnum ServiceCertificateConstraint = 1 | ||
44 | fromEnum TrustAnchorAssertion = 2 | ||
45 | fromEnum DomainIssued = 3 | ||
46 | fromEnum (CertUsage n) = fromEnum n | ||
47 | toEnum 0 = CAConstraint | ||
48 | toEnum 1 = ServiceCertificateConstraint | ||
49 | toEnum 2 = TrustAnchorAssertion | ||
50 | toEnum 3 = DomainIssued | ||
51 | toEnum n = CertUsage (toEnum n) | ||
52 | |||
53 | data Selector | ||
54 | = FullCertificate | ||
55 | | SubjectPublicKeyInfo | ||
56 | | Selector Word8 -- Selector 255 reserved for private use | ||
57 | deriving (Eq,Ord,Show) | ||
58 | |||
59 | instance Enum Selector where | ||
60 | fromEnum FullCertificate = 0 | ||
61 | fromEnum SubjectPublicKeyInfo = 1 | ||
62 | fromEnum (Selector n) = fromEnum n | ||
63 | toEnum 0 = FullCertificate | ||
64 | toEnum 1 = SubjectPublicKeyInfo | ||
65 | toEnum n = Selector (toEnum n) | ||
66 | |||
67 | data MatchingType | ||
68 | = Match_Exact | ||
69 | | Match_SHA256 | ||
70 | | Match_SHA512 | ||
71 | | Match Word8 -- Match 255 reserved for private use | ||
72 | deriving (Eq,Ord,Show) | ||
73 | |||
74 | instance Enum MatchingType where | ||
75 | fromEnum Match_Exact = 0 | ||
76 | fromEnum Match_SHA256 = 1 | ||
77 | fromEnum Match_SHA512 = 2 | ||
78 | fromEnum (Match n) = fromEnum n | ||
79 | toEnum 0 = Match_Exact | ||
80 | toEnum 1 = Match_SHA256 | ||
81 | toEnum 2 = Match_SHA512 | ||
82 | toEnum n = Match (toEnum n) | ||
83 | |||
84 | |||
85 | data TLSA = TLSA | ||
86 | { tlsaCertUsage :: CertUsage | ||
87 | , tlsaSelector :: Selector | ||
88 | , tlsaMatchingType :: MatchingType | ||
89 | , tlsaAssociationData :: BS.ByteString | ||
90 | } | ||
91 | deriving (Eq, Ord, Show) | ||
92 | |||
93 | fromByteString :: BS.ByteString -> TLSA | ||
94 | fromByteString bs = TLSA (fromWord8 cu) | ||
95 | (fromWord8 sel) | ||
96 | (fromWord8 mat) | ||
97 | associationData | ||
98 | where | ||
99 | (csm,associationData) = BS.splitAt 3 bs | ||
100 | (cu,sel,mat) = | ||
101 | case BS.unpack csm of | ||
102 | [cu,sel,mat] -> (cu,sel,mat) | ||
103 | [cu,sel] -> (cu,sel,0) | ||
104 | [cu] -> (cu,0,0) | ||
105 | [] -> (0,0,0) | ||
106 | |||
107 | toByteString :: TLSA -> BS.ByteString | ||
108 | toByteString (TLSA cu sel mat associationData) = csm <> associationData | ||
109 | where | ||
110 | csm = BS.pack [ toWord8 cu | ||
111 | , toWord8 sel | ||
112 | , toWord8 mat ] | ||
113 | |||
114 | match :: Certificate -> TLSA -> Bool | ||
115 | match cert tlsa = fromMaybe False $ | ||
116 | (== tlsaAssociationData tlsa) <$> (hash <*> material) | ||
117 | |||
118 | where | ||
119 | encode obj = encodeASN1 DER (toASN1 obj []) | ||
120 | |||
121 | material = | ||
122 | case tlsaSelector tlsa of | ||
123 | FullCertificate -> Just $ encode cert | ||
124 | SubjectPublicKeyInfo -> Just $ encode $ certPubKey cert | ||
125 | _ -> Nothing | ||
126 | |||
127 | hash = | ||
128 | case tlsaMatchingType tlsa of | ||
129 | Match_Exact -> Just L.toStrict | ||
130 | Match_SHA256 -> Just SHA256.hashlazy | ||
131 | Match_SHA512 -> Just SHA512.hashlazy | ||
132 | _ -> Nothing | ||
133 | |||