ByteArray.java

1
// Copyright (c) 2018, Yubico AB
2
// All rights reserved.
3
//
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are met:
6
//
7
// 1. Redistributions of source code must retain the above copyright notice, this
8
//    list of conditions and the following disclaimer.
9
//
10
// 2. Redistributions in binary form must reproduce the above copyright notice,
11
//    this list of conditions and the following disclaimer in the documentation
12
//    and/or other materials provided with the distribution.
13
//
14
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25
package com.yubico.webauthn.data;
26
27
import com.fasterxml.jackson.annotation.JsonCreator;
28
import com.fasterxml.jackson.annotation.JsonValue;
29
import com.google.common.primitives.Bytes;
30
import com.yubico.internal.util.BinaryUtil;
31
import com.yubico.webauthn.data.exception.Base64UrlException;
32
import com.yubico.webauthn.data.exception.HexException;
33
import java.util.Base64;
34
import lombok.EqualsAndHashCode;
35
import lombok.NonNull;
36
import lombok.ToString;
37
38
/** An immutable byte array with support for encoding/decoding to/from various encodings. */
39
@EqualsAndHashCode
40
@ToString(includeFieldNames = false, onlyExplicitlyIncluded = true)
41
public final class ByteArray implements Comparable<ByteArray> {
42
43
  private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
44
  private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
45
46
  private static final Base64.Encoder BASE64URL_ENCODER = Base64.getUrlEncoder().withoutPadding();
47
  private static final Base64.Decoder BASE64URL_DECODER = Base64.getUrlDecoder();
48
49
  @NonNull private final byte[] bytes;
50
51
  @JsonValue @NonNull private final String base64url;
52
53
  /** Create a new instance by copying the contents of <code>bytes</code>. */
54 1 1. <init> : negated conditional → KILLED
  public ByteArray(@NonNull byte[] bytes) {
55
    this.bytes = BinaryUtil.copy(bytes);
56
    this.base64url = BASE64URL_ENCODER.encodeToString(this.bytes);
57
  }
58
59
  private ByteArray(String base64url) throws Base64UrlException {
60
    try {
61
      this.bytes = BASE64URL_DECODER.decode(base64url);
62
    } catch (IllegalArgumentException e) {
63
      throw new Base64UrlException("Invalid Base64Url encoding: " + base64url, e);
64
    }
65
    this.base64url = base64url;
66
  }
67
68
  /** Create a new instance by decoding <code>base64</code> as classic Base64 data. */
69 1 1. fromBase64 : negated conditional → KILLED
  public static ByteArray fromBase64(@NonNull final String base64) {
70 1 1. fromBase64 : replaced return value with null for com/yubico/webauthn/data/ByteArray::fromBase64 → KILLED
    return new ByteArray(BASE64_DECODER.decode(base64));
71
  }
72
73
  /**
74
   * Create a new instance by decoding <code>base64url</code> as Base64Url data.
75
   *
76
   * @throws Base64UrlException if <code>base64url</code> is not valid Base64Url data.
77
   */
78
  @JsonCreator
79 1 1. fromBase64Url : negated conditional → KILLED
  public static ByteArray fromBase64Url(@NonNull final String base64url) throws Base64UrlException {
80 1 1. fromBase64Url : replaced return value with null for com/yubico/webauthn/data/ByteArray::fromBase64Url → KILLED
    return new ByteArray(base64url.split("=")[0]);
81
  }
82
83
  /**
84
   * Create a new instance by decoding <code>hex</code> as hexadecimal data.
85
   *
86
   * @throws HexException if <code>hex</code> is not valid hexadecimal data.
87
   */
88 1 1. fromHex : negated conditional → KILLED
  public static ByteArray fromHex(@NonNull final String hex) throws HexException {
89
    try {
90 1 1. fromHex : replaced return value with null for com/yubico/webauthn/data/ByteArray::fromHex → KILLED
      return new ByteArray(BinaryUtil.fromHex(hex));
91
    } catch (Exception e) {
92
      throw new HexException("Invalid hexadecimal encoding: " + hex, e);
93
    }
94
  }
95
96
  /**
97
   * @return a new instance containing a copy of this instance followed by a copy of <code>tail
98
   *     </code>.
99
   */
100 1 1. concat : negated conditional → KILLED
  public ByteArray concat(@NonNull ByteArray tail) {
101 1 1. concat : replaced return value with null for com/yubico/webauthn/data/ByteArray::concat → KILLED
    return new ByteArray(Bytes.concat(this.bytes, tail.bytes));
102
  }
103
104
  public boolean isEmpty() {
105 2 1. isEmpty : negated conditional → KILLED
2. isEmpty : replaced boolean return with true for com/yubico/webauthn/data/ByteArray::isEmpty → KILLED
    return size() == 0;
106
  }
107
108
  public int size() {
109 1 1. size : replaced int return with 0 for com/yubico/webauthn/data/ByteArray::size → KILLED
    return this.bytes.length;
110
  }
111
112
  /**
113
   * @return a copy of the raw byte contents.
114
   */
115
  public byte[] getBytes() {
116 1 1. getBytes : replaced return value with null for com/yubico/webauthn/data/ByteArray::getBytes → KILLED
    return BinaryUtil.copy(bytes);
117
  }
118
119
  /**
120
   * @return the content bytes encoded as classic Base64 data.
121
   */
122
  public String getBase64() {
123 1 1. getBase64 : replaced return value with "" for com/yubico/webauthn/data/ByteArray::getBase64 → KILLED
    return BASE64_ENCODER.encodeToString(bytes);
124
  }
125
126
  /**
127
   * @return the content bytes encoded as Base64Url data, without padding.
128
   */
129
  public String getBase64Url() {
130 1 1. getBase64Url : replaced return value with "" for com/yubico/webauthn/data/ByteArray::getBase64Url → KILLED
    return base64url;
131
  }
132
133
  /**
134
   * @return the content bytes encoded as hexadecimal data.
135
   */
136
  @ToString.Include
137
  public String getHex() {
138 1 1. getHex : replaced return value with "" for com/yubico/webauthn/data/ByteArray::getHex → KILLED
    return BinaryUtil.toHex(bytes);
139
  }
140
141
  @Override
142
  public int compareTo(ByteArray other) {
143 1 1. compareTo : negated conditional → KILLED
    if (bytes.length != other.bytes.length) {
144 2 1. compareTo : Replaced integer subtraction with addition → KILLED
2. compareTo : replaced int return with 0 for com/yubico/webauthn/data/ByteArray::compareTo → KILLED
      return bytes.length - other.bytes.length;
145
    }
146
147 2 1. compareTo : changed conditional boundary → KILLED
2. compareTo : negated conditional → KILLED
    for (int i = 0; i < bytes.length; ++i) {
148 1 1. compareTo : negated conditional → KILLED
      if (bytes[i] != other.bytes[i]) {
149 2 1. compareTo : Replaced integer subtraction with addition → KILLED
2. compareTo : replaced int return with 0 for com/yubico/webauthn/data/ByteArray::compareTo → KILLED
        return bytes[i] - other.bytes[i];
150
      }
151
    }
152
153
    return 0;
154
  }
155
}

Mutations

54

1.1
Location : <init>
Killed by : com.yubico.webauthn.data.ByteArrayTest.testEncodeBase64Url(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

69

1.1
Location : fromBase64
Killed by : com.yubico.webauthn.data.ByteArrayTest.codecMimeTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

70

1.1
Location : fromBase64
Killed by : com.yubico.webauthn.data.ByteArrayTest.codecMimeTest(com.yubico.webauthn.data.ByteArrayTest)
replaced return value with null for com/yubico/webauthn/data/ByteArray::fromBase64 → KILLED

79

1.1
Location : fromBase64Url
Killed by : com.yubico.webauthn.data.ByteArrayTest.decodeBadAlphabetTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

80

1.1
Location : fromBase64Url
Killed by : com.yubico.webauthn.data.ByteArrayTest.decodeTest(com.yubico.webauthn.data.ByteArrayTest)
replaced return value with null for com/yubico/webauthn/data/ByteArray::fromBase64Url → KILLED

88

1.1
Location : fromHex
Killed by : com.yubico.webauthn.data.ByteArrayTest.decodeBadHexLengthTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

90

1.1
Location : fromHex
Killed by : com.yubico.webauthn.data.ByteArrayTest.isEmptyTest(com.yubico.webauthn.data.ByteArrayTest)
replaced return value with null for com/yubico/webauthn/data/ByteArray::fromHex → KILLED

100

1.1
Location : concat
Killed by : com.yubico.webauthn.data.AuthenticatorDataSpec
negated conditional → KILLED

101

1.1
Location : concat
Killed by : com.yubico.webauthn.data.AuthenticatorDataSpec
replaced return value with null for com/yubico/webauthn/data/ByteArray::concat → KILLED

105

1.1
Location : isEmpty
Killed by : com.yubico.webauthn.data.ByteArrayTest.isEmptyTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

2.2
Location : isEmpty
Killed by : com.yubico.webauthn.data.ByteArrayTest.isEmptyTest(com.yubico.webauthn.data.ByteArrayTest)
replaced boolean return with true for com/yubico/webauthn/data/ByteArray::isEmpty → KILLED

109

1.1
Location : size
Killed by : com.yubico.webauthn.data.ByteArrayTest.isEmptyTest(com.yubico.webauthn.data.ByteArrayTest)
replaced int return with 0 for com/yubico/webauthn/data/ByteArray::size → KILLED

116

1.1
Location : getBytes
Killed by : com.yubico.webauthn.data.ByteArrayTest.decodeTest(com.yubico.webauthn.data.ByteArrayTest)
replaced return value with null for com/yubico/webauthn/data/ByteArray::getBytes → KILLED

123

1.1
Location : getBase64
Killed by : com.yubico.webauthn.data.ByteArrayTest.codecMimeTest(com.yubico.webauthn.data.ByteArrayTest)
replaced return value with "" for com/yubico/webauthn/data/ByteArray::getBase64 → KILLED

130

1.1
Location : getBase64Url
Killed by : com.yubico.webauthn.data.ByteArrayTest.testEncodeBase64Url(com.yubico.webauthn.data.ByteArrayTest)
replaced return value with "" for com/yubico/webauthn/data/ByteArray::getBase64Url → KILLED

138

1.1
Location : getHex
Killed by : com.yubico.webauthn.data.AuthenticatorDataSpec
replaced return value with "" for com/yubico/webauthn/data/ByteArray::getHex → KILLED

143

1.1
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

144

1.1
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
Replaced integer subtraction with addition → KILLED

2.2
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
replaced int return with 0 for com/yubico/webauthn/data/ByteArray::compareTo → KILLED

147

1.1
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
changed conditional boundary → KILLED

2.2
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

148

1.1
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
negated conditional → KILLED

149

1.1
Location : compareTo
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
Replaced integer subtraction with addition → KILLED

2.2
Location : compareTo
Killed by : com.yubico.webauthn.data.ByteArrayTest.sortTest(com.yubico.webauthn.data.ByteArrayTest)
replaced int return with 0 for com/yubico/webauthn/data/ByteArray::compareTo → KILLED

Active mutators

Tests examined


Report generated by PIT 1.15.0