Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/WebAuthnUtil.h"
8 : #include "pkixutil.h"
9 :
10 : namespace mozilla {
11 : namespace dom {
12 :
13 : nsresult
14 0 : ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
15 : uint32_t aLen)
16 : {
17 0 : if (aSrc.EnsureLength(aLen) != pkix::Success) {
18 0 : return NS_ERROR_DOM_UNKNOWN_ERR;
19 : }
20 :
21 0 : aDest.ClearAndRetainStorage();
22 :
23 0 : for (uint32_t offset = 0; offset < aLen; ++offset) {
24 : uint8_t b;
25 0 : if (aSrc.Read(b) != pkix::Success) {
26 0 : return NS_ERROR_DOM_UNKNOWN_ERR;
27 : }
28 0 : if (!aDest.AppendElement(b, mozilla::fallible)) {
29 0 : return NS_ERROR_OUT_OF_MEMORY;
30 : }
31 : }
32 :
33 0 : return NS_OK;
34 : }
35 :
36 : nsresult
37 0 : U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
38 : const CryptoBuffer& aRpIdHash,
39 : const CryptoBuffer& aSignatureData)
40 : {
41 : // The AuthenticatorData for U2F devices is the concatenation of the
42 : // RP ID with the output of the U2F Sign operation.
43 0 : if (aRpIdHash.Length() != 32) {
44 0 : return NS_ERROR_INVALID_ARG;
45 : }
46 :
47 0 : if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
48 0 : return NS_ERROR_OUT_OF_MEMORY;
49 : }
50 :
51 0 : if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
52 0 : return NS_ERROR_OUT_OF_MEMORY;
53 : }
54 :
55 0 : return NS_OK;
56 : }
57 :
58 : nsresult
59 0 : U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
60 : /* out */ CryptoBuffer& aPubKeyBuf,
61 : /* out */ CryptoBuffer& aKeyHandleBuf,
62 : /* out */ CryptoBuffer& aAttestationCertBuf,
63 : /* out */ CryptoBuffer& aSignatureBuf)
64 : {
65 : // U2F v1.1 Format via
66 : // http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
67 : //
68 : // Bytes Value
69 : // 1 0x05
70 : // 65 public key
71 : // 1 key handle length
72 : // * key handle
73 : // ASN.1 attestation certificate
74 : // * attestation signature
75 :
76 0 : pkix::Input u2fResponse;
77 0 : u2fResponse.Init(aResponse.Elements(), aResponse.Length());
78 :
79 0 : pkix::Reader input(u2fResponse);
80 :
81 : uint8_t b;
82 0 : if (input.Read(b) != pkix::Success) {
83 0 : return NS_ERROR_DOM_UNKNOWN_ERR;
84 : }
85 0 : if (b != 0x05) {
86 0 : return NS_ERROR_DOM_UNKNOWN_ERR;
87 : }
88 :
89 0 : nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
90 0 : if (NS_FAILED(rv)) {
91 0 : return rv;
92 : }
93 :
94 : uint8_t handleLen;
95 0 : if (input.Read(handleLen) != pkix::Success) {
96 0 : return NS_ERROR_DOM_UNKNOWN_ERR;
97 : }
98 :
99 0 : rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
100 0 : if (NS_FAILED(rv)) {
101 0 : return rv;
102 : }
103 :
104 : // We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
105 : // length.
106 0 : pkix::Input cert;
107 0 : if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
108 : != pkix::Success) {
109 0 : return NS_ERROR_DOM_UNKNOWN_ERR;
110 : }
111 :
112 0 : pkix::Reader certInput(cert);
113 0 : rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
114 0 : if (NS_FAILED(rv)) {
115 0 : return rv;
116 : }
117 :
118 : // The remainder of u2fResponse is the signature
119 0 : pkix::Input u2fSig;
120 0 : input.SkipToEnd(u2fSig);
121 0 : pkix::Reader sigInput(u2fSig);
122 0 : rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
123 0 : if (NS_FAILED(rv)) {
124 0 : return rv;
125 : }
126 :
127 0 : return NS_OK;
128 : }
129 :
130 : }
131 9 : }
|