from: github.com/remko/age-plugin-se

This commit is contained in:
2023-03-09 22:52:40 +08:00
parent 57fac2893c
commit 71927745a8
22 changed files with 2720 additions and 2 deletions

79
Tests/Base64Tests.swift Normal file
View File

@@ -0,0 +1,79 @@
import XCTest
@testable import age_plugin_se
final class Base64Tests: XCTestCase {
func testDataInitBase64RawEncoded_NeedsNoPad() throws {
XCTAssertEqual(
Data([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]),
Data(base64RawEncoded: "AQIDBAUG"))
}
func testDataInitBase64RawEncoded_Needs1Pad() throws {
XCTAssertEqual(
Data([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]),
Data(base64RawEncoded: "AQIDBAUGBwg"))
}
func testDataInitBase64RawEncoded_Needs2Pads() throws {
XCTAssertEqual(
Data([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
Data(base64RawEncoded: "AQIDBAUGBw"))
}
func testDataInitBase64RawEncoded_HasPad() throws {
XCTAssertEqual(
nil,
Data(base64RawEncoded: "AQIDBAUGBwg="))
}
func testDataInit_InvalidBase64() throws {
XCTAssertEqual(
nil,
Data(base64RawEncoded: "A_QIDBAUG"))
}
func testDataBase64RawEncodedData() throws {
XCTAssertEqual(
"AQIDBAUGBw".data(using: .utf8),
Data([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]).base64RawEncodedData)
}
func testDataBase64RawEncodedData_Long() throws {
XCTAssertEqual(
"""
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np
bmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFi
b3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVu
aWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBu
aXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0
ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxp
dCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBF
eGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBz
dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlk
IGVzdCBsYWJvcnVtLg
""".data(using: .utf8),
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
.data(using: .utf8)!
.base64RawEncodedData)
}
func testDataBase64RawEncodedString_Long() throws {
XCTAssertEqual(
"""
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np
bmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFi
b3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVu
aWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBu
aXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0
ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxp
dCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBF
eGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBz
dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlk
IGVzdCBsYWJvcnVtLg
""",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
.data(using: .utf8)!
.base64RawEncodedString)
}
}

103
Tests/Bech32Tests.swift Normal file
View File

@@ -0,0 +1,103 @@
import XCTest
@testable import age_plugin_se
final class Bech32Tests: XCTestCase {
func testEncode() throws {
XCTAssertEqual(
"age1se1qv9p3zge0tmqxczme5pn3p7g3x80t0uxvlk30s9vevjq0lxuy8rzss09jyq",
Bech32().encode(
hrp: "age1se",
data: Data([
0x03, 0x0a, 0x18, 0x89, 0x19, 0x7a, 0xf6, 0x03, 0x60, 0x5b, 0xcd, 0x03, 0x38, 0x87, 0xc8,
0x89, 0x8e, 0xf5, 0xbf, 0x86, 0x67, 0xed, 0x17, 0xc0, 0xac, 0xcb, 0x24, 0x07, 0xfc, 0xdc,
0x21, 0xc6, 0x28,
])))
}
func testEncode_LongUppercase() throws {
XCTAssertEqual(
"AGE-PLUGIN-SE-1QJPQZ7P3SGQHGVYP75XQYUNTXXQ7UVQTPSPKY6TYQSZDNVLMZYCYSRQRWP6KYPZPQNV20QEQRP3CLMWQALZ8V6TFESK6VDUL30F0D7TC2EXE2RV3Z2TQ5L0ZQFLHJMLY64XS8ESX6KFTL43MN86QVA0W982DTFWL4XMRT7CGXQYQCQMJDDHSYQGQXQRSCQNTWSPQZPPS9CXQYAMTQS5W4LASFLR432Z3658D86JEL8MKGE9XTJLHK4P3ASKWWZ8W6G7RWVLQYSWECSL8XF4RQPCVQF3XXQSPPYCQWRQZDDMQYQGZXQTSCQMTD9JQGYRALY9FHQYLGFET999CZUFGPGLJXQNSCQMJDDKSGGXE56G2EH3Y5V0QPUM8VHADV3FS2TKDR4F2M8266Y444ZFF3FW0VCC85RQZV4JRZAPSWGXQXCTRDSCKKVPFPSPK7CMTXY3RQGQVQD3HQMCVR9ZX2ANFVDJ57AMWV4EYZAT5DPJKUARFVDSHG6T0DCCQJRQYDAJX2MQPQYQNQ2SVQ3HHXEMWXY3RQGQVQD3HQMCVR9ZX2ANFVDJ57AMWV4EYZAT5DPJKUARFVDSHG6T0DCCQWRQZDASSZQGPK9FKDS",
Bech32().encode(
hrp: "AGE-PLUGIN-SE-",
data: Data([
0x04, 0x82, 0x01, 0x78, 0x31, 0x82, 0x01, 0x74, 0x30, 0x81, 0xf5, 0x0c, 0x02, 0x72, 0x6b,
0x31, 0x81, 0xee, 0x30, 0x0b, 0x0c, 0x03, 0x62, 0x69, 0x64, 0x04, 0x04, 0xd9, 0xb3, 0xfb,
0x11, 0x30, 0x48, 0x0c, 0x03, 0x70, 0x75, 0x62, 0x04, 0x41, 0x04, 0xd8, 0xa7, 0x83, 0x20,
0x18, 0x63, 0x8f, 0xed, 0xc0, 0xef, 0xc4, 0x76, 0x69, 0x69, 0xcc, 0x2d, 0xa6, 0x37, 0x9f,
0x8b, 0xd2, 0xf6, 0xf9, 0x78, 0x56, 0x4d, 0x95, 0x0d, 0x91, 0x12, 0x96, 0x0a, 0x7d, 0xe2,
0x02, 0x7f, 0x79, 0x6f, 0xe4, 0xd5, 0x4d, 0x03, 0xe6, 0x06, 0xd5, 0x92, 0xbf, 0xd6, 0x3b,
0x99, 0xf4, 0x06, 0x75, 0xee, 0x29, 0xd4, 0xd5, 0xa5, 0xdf, 0xa9, 0xb6, 0x35, 0xfb, 0x08,
0x30, 0x08, 0x0c, 0x03, 0x72, 0x6b, 0x6f, 0x02, 0x01, 0x00, 0x30, 0x07, 0x0c, 0x02, 0x6b,
0x74, 0x02, 0x01, 0x04, 0x30, 0x2e, 0x0c, 0x02, 0x77, 0x6b, 0x04, 0x28, 0xea, 0xff, 0xb0,
0x4f, 0xc7, 0x58, 0xa8, 0x51, 0xd5, 0x0e, 0xd3, 0xea, 0x59, 0xf9, 0xf7, 0x64, 0x64, 0xa6,
0x5c, 0xbf, 0x7b, 0x54, 0x31, 0xec, 0x2c, 0xe7, 0x08, 0xee, 0xd2, 0x3c, 0x37, 0x33, 0xe0,
0x24, 0x1d, 0x9c, 0x43, 0xe7, 0x32, 0x6a, 0x30, 0x07, 0x0c, 0x02, 0x62, 0x63, 0x02, 0x01,
0x09, 0x30, 0x07, 0x0c, 0x02, 0x6b, 0x76, 0x02, 0x01, 0x02, 0x30, 0x17, 0x0c, 0x03, 0x6b,
0x69, 0x64, 0x04, 0x10, 0x7d, 0xf9, 0x0a, 0x9b, 0x80, 0x9f, 0x42, 0x72, 0xb2, 0x94, 0xb8,
0x17, 0x12, 0x80, 0xa3, 0xf2, 0x30, 0x27, 0x0c, 0x03, 0x72, 0x6b, 0x6d, 0x04, 0x20, 0xd9,
0xa6, 0x90, 0xac, 0xde, 0x24, 0xa3, 0x1e, 0x00, 0xf3, 0x67, 0x65, 0xfa, 0xd6, 0x45, 0x30,
0x52, 0xec, 0xd1, 0xd5, 0x2a, 0xd9, 0xd5, 0xad, 0x12, 0xb5, 0xa8, 0x92, 0x98, 0xa5, 0xcf,
0x66, 0x30, 0x7a, 0x0c, 0x02, 0x65, 0x64, 0x31, 0x74, 0x30, 0x72, 0x0c, 0x03, 0x61, 0x63,
0x6c, 0x31, 0x6b, 0x30, 0x29, 0x0c, 0x03, 0x6f, 0x63, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x0c,
0x03, 0x63, 0x70, 0x6f, 0x0c, 0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x77, 0x6e,
0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x30, 0x09, 0x0c, 0x04, 0x6f, 0x64, 0x65, 0x6c, 0x01, 0x01, 0x01, 0x30, 0x2a, 0x0c,
0x04, 0x6f, 0x73, 0x67, 0x6e, 0x31, 0x22, 0x30, 0x20, 0x0c, 0x03, 0x63, 0x70, 0x6f, 0x0c,
0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x75, 0x74,
0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x07, 0x0c, 0x02,
0x6f, 0x61, 0x01, 0x01, 0x01,
])))
}
func testDecode() throws {
let result = try Bech32().decode(
"age1se1qv9p3zge0tmqxczme5pn3p7g3x80t0uxvlk30s9vevjq0lxuy8rzss09jyq")
XCTAssertEqual(
"age1se", result.hrp)
XCTAssertEqual(
Data([
0x03, 0x0a, 0x18, 0x89, 0x19, 0x7a, 0xf6, 0x03, 0x60, 0x5b, 0xcd, 0x03, 0x38, 0x87, 0xc8,
0x89, 0x8e, 0xf5, 0xbf, 0x86, 0x67, 0xed, 0x17, 0xc0, 0xac, 0xcb, 0x24, 0x07, 0xfc, 0xdc,
0x21, 0xc6, 0x28,
]), result.data)
}
func testDecode_LongUppercase() throws {
let result = try Bech32().decode(
"AGE-PLUGIN-SE-1QJPQZ7P3SGQHGVYP75XQYUNTXXQ7UVQTPSPKY6TYQSZDNVLMZYCYSRQRWP6KYPZPQNV20QEQRP3CLMWQALZ8V6TFESK6VDUL30F0D7TC2EXE2RV3Z2TQ5L0ZQFLHJMLY64XS8ESX6KFTL43MN86QVA0W982DTFWL4XMRT7CGXQYQCQMJDDHSYQGQXQRSCQNTWSPQZPPS9CXQYAMTQS5W4LASFLR432Z3658D86JEL8MKGE9XTJLHK4P3ASKWWZ8W6G7RWVLQYSWECSL8XF4RQPCVQF3XXQSPPYCQWRQZDDMQYQGZXQTSCQMTD9JQGYRALY9FHQYLGFET999CZUFGPGLJXQNSCQMJDDKSGGXE56G2EH3Y5V0QPUM8VHADV3FS2TKDR4F2M8266Y444ZFF3FW0VCC85RQZV4JRZAPSWGXQXCTRDSCKKVPFPSPK7CMTXY3RQGQVQD3HQMCVR9ZX2ANFVDJ57AMWV4EYZAT5DPJKUARFVDSHG6T0DCCQJRQYDAJX2MQPQYQNQ2SVQ3HHXEMWXY3RQGQVQD3HQMCVR9ZX2ANFVDJ57AMWV4EYZAT5DPJKUARFVDSHG6T0DCCQWRQZDASSZQGPK9FKDS"
)
XCTAssertEqual(
"AGE-PLUGIN-SE-", result.hrp)
// print(result.data.map { String(format: "0x%02x", $0) }.joined(separator: ", "))
XCTAssertEqual(
Data([
0x04, 0x82, 0x01, 0x78, 0x31, 0x82, 0x01, 0x74, 0x30, 0x81, 0xf5, 0x0c, 0x02, 0x72, 0x6b,
0x31, 0x81, 0xee, 0x30, 0x0b, 0x0c, 0x03, 0x62, 0x69, 0x64, 0x04, 0x04, 0xd9, 0xb3, 0xfb,
0x11, 0x30, 0x48, 0x0c, 0x03, 0x70, 0x75, 0x62, 0x04, 0x41, 0x04, 0xd8, 0xa7, 0x83, 0x20,
0x18, 0x63, 0x8f, 0xed, 0xc0, 0xef, 0xc4, 0x76, 0x69, 0x69, 0xcc, 0x2d, 0xa6, 0x37, 0x9f,
0x8b, 0xd2, 0xf6, 0xf9, 0x78, 0x56, 0x4d, 0x95, 0x0d, 0x91, 0x12, 0x96, 0x0a, 0x7d, 0xe2,
0x02, 0x7f, 0x79, 0x6f, 0xe4, 0xd5, 0x4d, 0x03, 0xe6, 0x06, 0xd5, 0x92, 0xbf, 0xd6, 0x3b,
0x99, 0xf4, 0x06, 0x75, 0xee, 0x29, 0xd4, 0xd5, 0xa5, 0xdf, 0xa9, 0xb6, 0x35, 0xfb, 0x08,
0x30, 0x08, 0x0c, 0x03, 0x72, 0x6b, 0x6f, 0x02, 0x01, 0x00, 0x30, 0x07, 0x0c, 0x02, 0x6b,
0x74, 0x02, 0x01, 0x04, 0x30, 0x2e, 0x0c, 0x02, 0x77, 0x6b, 0x04, 0x28, 0xea, 0xff, 0xb0,
0x4f, 0xc7, 0x58, 0xa8, 0x51, 0xd5, 0x0e, 0xd3, 0xea, 0x59, 0xf9, 0xf7, 0x64, 0x64, 0xa6,
0x5c, 0xbf, 0x7b, 0x54, 0x31, 0xec, 0x2c, 0xe7, 0x08, 0xee, 0xd2, 0x3c, 0x37, 0x33, 0xe0,
0x24, 0x1d, 0x9c, 0x43, 0xe7, 0x32, 0x6a, 0x30, 0x07, 0x0c, 0x02, 0x62, 0x63, 0x02, 0x01,
0x09, 0x30, 0x07, 0x0c, 0x02, 0x6b, 0x76, 0x02, 0x01, 0x02, 0x30, 0x17, 0x0c, 0x03, 0x6b,
0x69, 0x64, 0x04, 0x10, 0x7d, 0xf9, 0x0a, 0x9b, 0x80, 0x9f, 0x42, 0x72, 0xb2, 0x94, 0xb8,
0x17, 0x12, 0x80, 0xa3, 0xf2, 0x30, 0x27, 0x0c, 0x03, 0x72, 0x6b, 0x6d, 0x04, 0x20, 0xd9,
0xa6, 0x90, 0xac, 0xde, 0x24, 0xa3, 0x1e, 0x00, 0xf3, 0x67, 0x65, 0xfa, 0xd6, 0x45, 0x30,
0x52, 0xec, 0xd1, 0xd5, 0x2a, 0xd9, 0xd5, 0xad, 0x12, 0xb5, 0xa8, 0x92, 0x98, 0xa5, 0xcf,
0x66, 0x30, 0x7a, 0x0c, 0x02, 0x65, 0x64, 0x31, 0x74, 0x30, 0x72, 0x0c, 0x03, 0x61, 0x63,
0x6c, 0x31, 0x6b, 0x30, 0x29, 0x0c, 0x03, 0x6f, 0x63, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x0c,
0x03, 0x63, 0x70, 0x6f, 0x0c, 0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x77, 0x6e,
0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x30, 0x09, 0x0c, 0x04, 0x6f, 0x64, 0x65, 0x6c, 0x01, 0x01, 0x01, 0x30, 0x2a, 0x0c,
0x04, 0x6f, 0x73, 0x67, 0x6e, 0x31, 0x22, 0x30, 0x20, 0x0c, 0x03, 0x63, 0x70, 0x6f, 0x0c,
0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x75, 0x74,
0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x07, 0x0c, 0x02,
0x6f, 0x61, 0x01, 0x01, 0x01,
]), result.data)
}
}

49
Tests/CLITests.swift Normal file
View File

@@ -0,0 +1,49 @@
import XCTest
@testable import age_plugin_se
final class OptionsTests: XCTestCase {
func testParse_NoArguments() throws {
let options = try Options.parse(["_"])
XCTAssertEqual(.help, options.command)
}
func testParse_CommandWithHelp() throws {
let options = try Options.parse(["_", "keygen", "--help"])
XCTAssertEqual(.help, options.command)
}
func testParse_CommandWithVersion() throws {
let options = try Options.parse(["_", "keygen", "--version"])
XCTAssertEqual(.version, options.command)
}
func testParse_Keygen() throws {
let options = try Options.parse(["_", "keygen", "--access-control=any-biometry"])
XCTAssertEqual(.keygen, options.command)
XCTAssertEqual(.anyBiometry, options.accessControl)
}
func testParse_AgePlugin() throws {
let options = try Options.parse(["_", "keygen", "--age-plugin=identity-v1"])
XCTAssertEqual(.plugin(.identityV1), options.command)
}
func testParse_LongOptionWithEqual() throws {
let options = try Options.parse(["_", "keygen", "--output=foo.txt"])
XCTAssertEqual(.keygen, options.command)
XCTAssertEqual("foo.txt", options.output)
}
func testParse_LongOptionWithoutEqual() throws {
let options = try Options.parse(["_", "keygen", "--output", "foo.txt"])
XCTAssertEqual(.keygen, options.command)
XCTAssertEqual("foo.txt", options.output)
}
func testParse_LongOptionWithoutValue() throws {
XCTAssertThrowsError(try Options.parse(["_", "keygen", "--output"])) { error in
XCTAssertEqual(Options.Error.missingValue("--output"), error as! Options.Error)
}
}
}

71
Tests/CryptoTests.swift Normal file
View File

@@ -0,0 +1,71 @@
import XCTest
@testable import age_plugin_se
#if !os(Linux) && !os(Windows)
import CryptoKit
#else
import Crypto
#endif
final class CryptoKitCryptoTests: XCTestCase {
var crypto = CryptoKitCrypto()
func testNewEphemeralPrivateKey() throws {
let k1 = crypto.newEphemeralPrivateKey()
let k2 = crypto.newEphemeralPrivateKey()
XCTAssertNotEqual(k1.rawRepresentation, k2.rawRepresentation)
XCTAssertNotEqual(k1.publicKey.rawRepresentation, k2.publicKey.rawRepresentation)
}
func testNewEphemeralPrivateKey_DifferentCrypto() throws {
let k1 = CryptoKitCrypto().newEphemeralPrivateKey()
let k2 = CryptoKitCrypto().newEphemeralPrivateKey()
XCTAssertNotEqual(k1.rawRepresentation, k2.rawRepresentation)
XCTAssertNotEqual(k1.publicKey.rawRepresentation, k2.publicKey.rawRepresentation)
}
// A test to validate that CryptoKit / Swift Crypto cannot do any operations with points at infinity
func testPointAtInfinity() throws {
let sk = P256.KeyAgreement.PrivateKey()
// base64.b64encode(ECC.generate(curve="p256").export_key(format="DER"))
let pk = try P256.KeyAgreement.PublicKey(derRepresentation: Data(base64Encoded: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0Zl262mVCr+1pi9396tEdXC0HIQnENUkWal3nOzLWvX+TYja1xVE++6WzRvunrkBT91380BIJZvB7ZiiEN+Y1A==")!)
// Test that operations work from a regular DER constructed key
let _ = try sk.sharedSecretFromKeyAgreement(with: pk)
func run() throws {
// base64.b64encode(ECC.EccKey(curve = "p256", point = ECC.generate(curve="p256").pointQ.point_at_infinity()).export_key(format="DER"))
// Swift Crypto throws at construction time
let identityPK = try P256.KeyAgreement.PublicKey(derRepresentation: Data(base64Encoded: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")!)
// CryptoKit throws at operation time
let _ = try sk.sharedSecretFromKeyAgreement(with: identityPK)
}
XCTAssertThrowsError(try run())
}
}
final class DummyCryptoTests: XCTestCase {
var crypto = DummyCrypto()
func testNewEphemeralPrivateKey() throws {
let k1 = crypto.newEphemeralPrivateKey()
let k2 = crypto.newEphemeralPrivateKey()
XCTAssertNotEqual(k1.rawRepresentation, k2.rawRepresentation)
XCTAssertNotEqual(k1.publicKey.rawRepresentation, k2.publicKey.rawRepresentation)
}
func testNewEphemeralPrivateKey_DifferentCrypto() throws {
let k1 = DummyCrypto().newEphemeralPrivateKey()
let k2 = DummyCrypto().newEphemeralPrivateKey()
XCTAssertEqual(k1.rawRepresentation, k2.rawRepresentation)
XCTAssertEqual(k1.publicKey.rawRepresentation, k2.publicKey.rawRepresentation)
}
}

75
Tests/DummyCrypto.swift Normal file
View File

@@ -0,0 +1,75 @@
import Foundation
@testable import age_plugin_se
#if !os(Linux) && !os(Windows)
import CryptoKit
#else
import Crypto
#endif
class DummyCrypto: Crypto {
// If more keys are needed, add them to the front
var dummyKeys = [
"t8Y0uUHLtBvCtuUz0Hdw2lqbwZf6TgYzYKFWMEEFSs8",
"HxEmObcQ6bcAUC8w6kPWrnlUIwBQoi66ZNpQZ0cAXww",
"dCDteyAKpkwYd8jCunOz0mvWmy+24zvWV41YBD+Pkeg",
"NkkLXSZ+yhx9imKKw9cOsbey4C1XZAPuSDMCgTLENrY",
"bQrp04tXb+diJ6x28Kd8EDt9sCmI5diS36Zy3n49DHg",
"m8/qMMkYDelvL+ihdUFYyKXBn+7We21fZ5zH/I61y3M",
"lQq/Pq0GA2QFGTEiNMQIxZHzBnt+nPRXK5gL3X6nnJY",
"VoUn+n/vzkuDzWgMV9n3e1L+tTSIl0Sg7lXSNDR5XqY",
"3naom0zZxBZcSZCfoNzyjLVmG6hyRKX8bCU3wukusFI",
"N2WRutxd1Ed0l4piqArI2gKYSTG7peE8BYBrLLV7YjQ",
].map { Data(base64RawEncoded: $0)! }
var isSecureEnclaveAvailable = true
var failingOperations = false
func newSecureEnclavePrivateKey(dataRepresentation: Data) throws -> SecureEnclavePrivateKey {
return DummySecureEnclavePrivateKey(
key: try P256.KeyAgreement.PrivateKey(rawRepresentation: dataRepresentation), crypto: self)
}
func newSecureEnclavePrivateKey(accessControl: SecAccessControl) throws -> SecureEnclavePrivateKey
{
return DummySecureEnclavePrivateKey(
key: try P256.KeyAgreement.PrivateKey(rawRepresentation: dummyKeys.popLast()!), crypto: self)
}
func newEphemeralPrivateKey() -> P256.KeyAgreement.PrivateKey {
return try! P256.KeyAgreement.PrivateKey(rawRepresentation: dummyKeys.popLast()!)
}
}
struct DummySecureEnclavePrivateKey: SecureEnclavePrivateKey {
var key: P256.KeyAgreement.PrivateKey
var crypto: DummyCrypto
var publicKey: P256.KeyAgreement.PublicKey {
return key.publicKey
}
var dataRepresentation: Data {
return key.rawRepresentation
}
func sharedSecretFromKeyAgreement(with publicKeyShare: P256.KeyAgreement.PublicKey) throws
-> SharedSecret
{
if crypto.failingOperations {
throw DummyCryptoError.dummyError
}
return try key.sharedSecretFromKeyAgreement(with: publicKeyShare)
}
}
enum DummyCryptoError: LocalizedError {
case dummyError
public var errorDescription: String? {
switch self {
case .dummyError: return "dummy error"
}
}
}

27
Tests/MemoryStream.swift Normal file
View File

@@ -0,0 +1,27 @@
@testable import age_plugin_se
class MemoryStream: Stream {
var inputLines: [String] = []
var outputLines: [String] = []
var output: String {
return outputLines.joined(separator: "\n")
}
func add(input: String) {
inputLines.append(contentsOf: input.components(separatedBy: "\n"))
}
func readLine() -> String? {
if inputLines.isEmpty {
return nil
}
let result = inputLines[0]
inputLines.removeFirst()
return result
}
func writeLine(_ line: String) {
outputLines.append(contentsOf: line.components(separatedBy: "\n"))
}
}

828
Tests/PluginTests.swift Normal file
View File

@@ -0,0 +1,828 @@
import XCTest
@testable import age_plugin_se
#if !os(Linux) && !os(Windows)
import CryptoKit
#else
import Crypto
#endif
final class PluginTests: XCTestCase {
func testCertificateTag() throws {
let key = try P256.KeyAgreement.PublicKey(compactRepresentation: Data(count: 32))
XCTAssertEqual("Ujulpw", key.tag.base64RawEncodedString)
}
// Test to ensure that age-plugin-yubikey has the same output tag
// These values were extracted from a yubikey recipient
func testCertificateTag_YubiKeyPlugin() throws {
let key = try P256.KeyAgreement.PublicKey(
compactRepresentation: Data([
182, 32, 36, 98, 119, 204, 123, 231, 20, 203, 102, 119, 81, 232, 194, 196, 140, 194, 55,
12, 222, 162, 205, 252, 47, 114, 187, 157, 117, 151, 57, 158,
]))
XCTAssertEqual(Data([128, 103, 102, 255]), key.tag)
XCTAssertEqual("gGdm/w", key.tag.base64RawEncodedString)
}
}
final class GenerateTests: XCTestCase {
var stream = MemoryStream()
var crypto = DummyCrypto()
override func setUp() {
stream = MemoryStream()
crypto = DummyCrypto()
}
func testGenerate() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
let result = try plugin.generateKey(
accessControl: .anyBiometryOrPasscode, now: Date(timeIntervalSinceReferenceDate: -123456789.0)
)
XCTAssertEqual(
"""
# created: 1997-02-02T02:26:51Z
# access control: any biometry or passcode
# public key: age1se1qvlvs7x2g83gtaqg0dlstnm3ee8tr49dhtdnxudpfd0sy2gedw20kjmseq4
AGE-PLUGIN-SE-1XAJERWKUTH2YWAYH3F32SZKGMGPFSJF3HWJ7Z0Q9SP4JEDTMVG6Q6JD2VG
""", result.0)
XCTAssertEqual(
"age1se1qvlvs7x2g83gtaqg0dlstnm3ee8tr49dhtdnxudpfd0sy2gedw20kjmseq4", result.1)
}
func testGenerate_AnyBiometryAndPasscode() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
let result = try plugin.generateKey(
accessControl: .anyBiometryAndPasscode,
now: Date(timeIntervalSinceReferenceDate: -123456789.0))
XCTAssertEqual(
"""
# created: 1997-02-02T02:26:51Z
# access control: any biometry and passcode
# public key: age1se1qvlvs7x2g83gtaqg0dlstnm3ee8tr49dhtdnxudpfd0sy2gedw20kjmseq4
AGE-PLUGIN-SE-1XAJERWKUTH2YWAYH3F32SZKGMGPFSJF3HWJ7Z0Q9SP4JEDTMVG6Q6JD2VG
""", result.0)
XCTAssertEqual(
"age1se1qvlvs7x2g83gtaqg0dlstnm3ee8tr49dhtdnxudpfd0sy2gedw20kjmseq4", result.1)
}
func testGenerate_CurrentBiometry() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
let result = try plugin.generateKey(
accessControl: .currentBiometry, now: Date(timeIntervalSinceReferenceDate: -123456789.0))
XCTAssertEqual(
"""
# created: 1997-02-02T02:26:51Z
# access control: current biometry
# public key: age1se1qvlvs7x2g83gtaqg0dlstnm3ee8tr49dhtdnxudpfd0sy2gedw20kjmseq4
AGE-PLUGIN-SE-1XAJERWKUTH2YWAYH3F32SZKGMGPFSJF3HWJ7Z0Q9SP4JEDTMVG6Q6JD2VG
""", result.0)
XCTAssertEqual(
"age1se1qvlvs7x2g83gtaqg0dlstnm3ee8tr49dhtdnxudpfd0sy2gedw20kjmseq4", result.1)
}
func testGenerate_NoSecureEnclaveSupport() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
crypto.isSecureEnclaveAvailable = false
XCTAssertThrowsError(
try plugin.generateKey(
accessControl: .anyBiometryOrPasscode,
now: Date(timeIntervalSinceReferenceDate: -123456789.0))
) { error in
XCTAssertEqual(Plugin.Error.seUnsupported, error as! Plugin.Error)
}
}
}
final class RecipientV1Tests: XCTestCase {
var stream = MemoryStream()
var crypto = DummyCrypto()
override func setUp() {
stream = MemoryStream()
crypto = DummyCrypto()
}
// Just a test to get the identities of the test keys used in this test
func testKeys() throws {
let key1 = try! crypto.newSecureEnclavePrivateKey(
dataRepresentation: Data(base64RawEncoded: "OSe+zDK18qF0UrjxYVkmwvxyEdxZHp9F69rElj8bKS8")!)
XCTAssertEqual(
"AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG",
key1.ageIdentity)
XCTAssertEqual(
"age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l",
key1.publicKey.ageRecipient)
let key2 = try! crypto.newSecureEnclavePrivateKey(
dataRepresentation: Data(base64RawEncoded: "kBuQrPyfvCqBXJ5G4YBkqNER201niIeOmlXsRS2gxN0")!)
XCTAssertEqual(
"AGE-PLUGIN-SE-1JQDEPT8UN77Z4Q2UNERWRQRY4RG3RK6DV7YG0R562HKY2TDQCNWSREKAW7",
key2.ageIdentity)
XCTAssertEqual(
"age1se1q0mm28s88km3d8fvwve26xg4tt26cqamhxm79g9xvmw0f2erawj752upj6l",
key2.publicKey.ageRecipient)
}
func testNothing() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(input: "-> done\n")
plugin.runRecipientV1()
XCTAssertEqual("-> done\n", stream.output)
}
func testRecipient() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-recipient age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> done
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
""", stream.output)
}
func testIdentity() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> done
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
""", stream.output)
}
func testMultipleRecipients() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-recipient age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> add-recipient age1se1q0mm28s88km3d8fvwve26xg4tt26cqamhxm79g9xvmw0f2erawj752upj6l
-> done
-> ok
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 0 piv-p256 1mgwOA A1x2nUpw2wo/7z0JR5puskK6NuvW5XkQBwkun/T3WC80
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> done
""", stream.output)
}
func testMultipleRecipientsMultipleKeys() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-recipient age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAg
-> add-recipient age1se1q0mm28s88km3d8fvwve26xg4tt26cqamhxm79g9xvmw0f2erawj752upj6l
-> done
-> ok
-> ok
-> ok
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 0 piv-p256 1mgwOA A1x2nUpw2wo/7z0JR5puskK6NuvW5XkQBwkun/T3WC80
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> recipient-stanza 1 piv-p256 14yi6A AvEp8Oz0cMnXhpXnWM6cwer4nEDHus/AvNp3kYnUH0Qs
L3ig8s2AqjusH/0lW6ZueSEYhpeV2ofrQpaKP06WI9g
-> recipient-stanza 1 piv-p256 1mgwOA AoIMpSYaKzGl5IBFaM9AFJXmrseGzTzcQjS9R4kRcjRi
vm8flaP+4W08S6LwFENwnEKLlpzZ5YqZ3NdpKFo7Vg8
-> done
""", stream.output)
}
func testRecipientError() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-recipient age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> add-recipient age1invalid1q0mm28s88km3d8fvwve26xg4tt26cqamhxm79g9xvmw0f2erawj75hkckfk
-> done
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> error recipient 1
Q2hlY2tzdW0gZG9lc24ndCBtYXRjaA
-> done
""", stream.output)
}
func testIdentityError() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> add-identity AGE-PLUGIN-INVALID-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHS2FM3SW
-> done
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> error identity 1
Q2hlY2tzdW0gZG9lc24ndCBtYXRjaA
-> done
""", stream.output)
}
func testInvalidRecipientHRP() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-recipient age1vld7p2khw44ds8t00vcfmjdf35zxqvn2trjccd35h4s22faj94vsjhn620
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> done
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> error recipient 0
dW5rbm93biBIUlA6IGFnZQ
-> done
""", stream.output)
}
// func testFailingCryptoOperations() throws {
// let plugin = Plugin(crypto: crypto, stream: stream)
// stream.add(
// input:
// """
// -> add-recipient age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l
// -> wrap-file-key
// AAAAAAAAAAAAAAAAAAAAAQ
// -> done
// -> ok
// """)
// crypto.failingOperations = true
// plugin.runRecipientV1()
// XCTAssertEqual(
// """
// -> error internal
// ZHVtbXkgZXJyb3I
// -> done
// """, stream.output)
// }
func testUnknownStanzaTypes() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-recipient age1se1qf0l9gks6x65ha077wq3w3u8fy02tpg3cd9w5j0jlgpfgqkcut2lw6hta9l
-> unknown-stanza 1 2 3
-> wrap-file-key
AAAAAAAAAAAAAAAAAAAAAQ
-> anotherunknownstanza
AAAAAAAAAAAAAAAAAAAAAQ
-> done
-> ok
""")
plugin.runRecipientV1()
XCTAssertEqual(
"""
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
""", stream.output)
}
}
final class IdentityV1Tests: XCTestCase {
var stream = MemoryStream()
var crypto = DummyCrypto()
override func setUp() {
stream = MemoryStream()
crypto = DummyCrypto()
}
func testNothing() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(input: "-> done\n")
plugin.runIdentityV1()
XCTAssertEqual("-> done\n", stream.output)
}
func testRecipientStanza() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> file-key 0
AAAAAAAAAAAAAAAAAAAAAQ
-> done
""", stream.output)
}
func testRecipientStanzaMultipleFiles() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 1 piv-p256 14yi6A AvEp8Oz0cMnXhpXnWM6cwer4nEDHus/AvNp3kYnUH0Qs
L3ig8s2AqjusH/0lW6ZueSEYhpeV2ofrQpaKP06WI9g
-> done
-> ok
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> file-key 0
AAAAAAAAAAAAAAAAAAAAAQ
-> file-key 1
AAAAAAAAAAAAAAAAAAAAAg
-> done
""", stream.output)
}
func testRecipientStanzaMultipleFilesMultipleIdentities() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> add-identity AGE-PLUGIN-SE-1JQDEPT8UN77Z4Q2UNERWRQRY4RG3RK6DV7YG0R562HKY2TDQCNWSREKAW7
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 0 piv-p256 1mgwOA A1x2nUpw2wo/7z0JR5puskK6NuvW5XkQBwkun/T3WC80
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> recipient-stanza 1 piv-p256 14yi6A AvEp8Oz0cMnXhpXnWM6cwer4nEDHus/AvNp3kYnUH0Qs
L3ig8s2AqjusH/0lW6ZueSEYhpeV2ofrQpaKP06WI9g
-> recipient-stanza 1 piv-p256 1mgwOA AoIMpSYaKzGl5IBFaM9AFJXmrseGzTzcQjS9R4kRcjRi
vm8flaP+4W08S6LwFENwnEKLlpzZ5YqZ3NdpKFo7Vg8
-> done
-> ok
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> file-key 0
AAAAAAAAAAAAAAAAAAAAAQ
-> file-key 1
AAAAAAAAAAAAAAAAAAAAAg
-> done
""", stream.output)
}
func testRecipientStanzaMultipleStanzasMissingIdentity() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-1JQDEPT8UN77Z4Q2UNERWRQRY4RG3RK6DV7YG0R562HKY2TDQCNWSREKAW7
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 0 piv-p256 1mgwOA A1x2nUpw2wo/7z0JR5puskK6NuvW5XkQBwkun/T3WC80
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> done
-> ok
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> file-key 0
AAAAAAAAAAAAAAAAAAAAAQ
-> done
""", stream.output)
}
func testRecipientStanza_UnknownType() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 X25519 A1x2nUpw2wo/7z0JR5puskK6NuvW5XkQBwkun/T3WC80
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> file-key 0
AAAAAAAAAAAAAAAAAAAAAQ
-> done
""", stream.output)
}
func testIdentityError() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> add-identity AGE-PLUGIN-INVALID-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHS2FM3SW
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error identity 1
Q2hlY2tzdW0gZG9lc24ndCBtYXRjaA
-> done
""", stream.output)
}
func testUnknownIdentityHRP() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> add-identity AGE-SECRET-KEY-1MCFVWZK6PK625PWMWVYPZDQM4N7AS3VA754JHCC60ZT7WJ79TQQSQDYVGF
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error identity 1
dW5rbm93biBIUlA6IEFHRS1TRUNSRVQtS0VZLQ
-> done
""", stream.output)
}
func testRecipientStanzaMultipleFilesStructurallyInvalidFile() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> add-identity AGE-PLUGIN-SE-1JQDEPT8UN77Z4Q2UNERWRQRY4RG3RK6DV7YG0R562HKY2TDQCNWSREKAW7
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 0 piv-p256 1mgwOA
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> recipient-stanza 1 piv-p256 14yi6A AvEp8Oz0cMnXhpXnWM6cwer4nEDHus/AvNp3kYnUH0Qs
L3ig8s2AqjusH/0lW6ZueSEYhpeV2ofrQpaKP06WI9g
-> recipient-stanza 1 piv-p256 1mgwOA AoIMpSYaKzGl5IBFaM9AFJXmrseGzTzcQjS9R4kRcjRi
vm8flaP+4W08S6LwFENwnEKLlpzZ5YqZ3NdpKFo7Vg8
-> done
-> ok
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error stanza 0
aW5jb3JyZWN0IGFyZ3VtZW50IGNvdW50
-> file-key 1
AAAAAAAAAAAAAAAAAAAAAg
-> done
""", stream.output)
}
func testRecipientStanzaInvalidStructure_ArgumentCount() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 piv-p256 1mgwOA
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error stanza 0
aW5jb3JyZWN0IGFyZ3VtZW50IGNvdW50
-> done
""", stream.output)
}
func testRecipientStanzaInvalidTag() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 piv-p256 14yi Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error stanza 0
aW52YWxpZCB0YWc
-> done
""", stream.output)
}
func testRecipientStanzaInvalidShare() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5Q
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error stanza 0
aW52YWxpZCBzaGFyZQ
-> done
""", stream.output)
}
func testRecipientStanzaInvalidBody() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
AAAAAAAAAAAAAAAAAAAAARIiJq2e9+1E+xK92Pvdtw
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> error stanza 0
aW52YWxpZCBib2R5
-> done
""", stream.output)
}
func testFailingCryptoOperations() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> add-identity AGE-PLUGIN-SE-1JQDEPT8UN77Z4Q2UNERWRQRY4RG3RK6DV7YG0R562HKY2TDQCNWSREKAW7
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> recipient-stanza 0 piv-p256 1mgwOA A1x2nUpw2wo/7z0JR5puskK6NuvW5XkQBwkun/T3WC80
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> done
-> ok
-> ok
-> ok
""")
crypto.failingOperations = true
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> msg
ZHVtbXkgZXJyb3I
-> msg
ZHVtbXkgZXJyb3I
-> done
""", stream.output)
}
func testUnknownStanzas() throws {
let plugin = Plugin(crypto: crypto, stream: stream)
stream.add(
input:
"""
-> unknown-stanza-1 a bbb c
-> add-identity AGE-PLUGIN-SE-18YNMANPJKHE2ZAZJHRCKZKFXCT78YYWUTY0F730TMTZFV0CM9YHSRP8GPG
-> unknown-stanza-2
9NGkkBZykDMgw6dndbbjnn7DQBalVV4sVIurWku030Y
-> recipient-stanza 0 piv-p256 14yi6A Az7IeMpB4oX0CHt/Bc9xzk6x1K262zNxoUtfAikZa5T7
SLgnrcnHLaJHCx+fwSEWWoflDgL91oDGCGNwb2YaT+4
-> done
-> ok
""")
plugin.runIdentityV1()
XCTAssertEqual(
"""
-> file-key 0
AAAAAAAAAAAAAAAAAAAAAQ
-> done
""", stream.output)
}
}

208
Tests/StanzaTests.swift Normal file
View File

@@ -0,0 +1,208 @@
import XCTest
@testable import age_plugin_se
final class StanzaTests: XCTestCase {
var stream = MemoryStream()
override func setUp() {
stream = MemoryStream()
}
func testReadFrom() throws {
stream.add(
input:
"""
-> mytype MyArgument1 MyArgument2
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np
bmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFi
b3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVu
aWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBu
aXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0
ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxp
dCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBF
eGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBz
dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlk
IGVzdCBsYWJvcnVtLg
""")
XCTAssertEqual(
Stanza(
type: "mytype",
args: ["MyArgument1", "MyArgument2"],
body:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
.data(using: .utf8)!
), try Stanza.readFrom(stream: stream))
}
func testReadFrom_EmptyBody() throws {
stream.add(
input:
"""
-> mytype
""")
XCTAssertEqual(
Stanza(
type: "mytype",
args: [],
body: Data()
), try Stanza.readFrom(stream: stream))
}
func testReadFrom_EmptyLastLine() throws {
stream.add(
input:
"""
-> mystanza
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np
""")
XCTAssertEqual(
Stanza(
type: "mystanza",
args: [],
body:
"Lorem ipsum dolor sit amet, consectetur adipisci"
.data(using: .utf8)!
), try Stanza.readFrom(stream: stream))
}
func testReadFrom_MissingType() throws {
stream.add(
input:
"""
->
IGVzdCBsYWJvcnVtLg
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.invalidStanza)
}
}
func testReadFrom_InvalidPrefix() throws {
stream.add(
input:
"""
=> mystanza
IGVzdCBsYWJvcnVtLg
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.invalidStanza)
}
}
func testReadFrom_BodyTooLong() throws {
stream.add(
input:
"""
-> mystanza
dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.invalidStanza)
}
}
func testReadFrom_BodyInvalid() throws {
stream.add(
input:
"""
-> mystanza
_dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.invalidStanza)
}
}
func testReadFrom_BodyIncomplete() throws {
stream.add(
input:
"""
-> mystanza
dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlk
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.incompleteStanza)
}
}
func testReadFrom_BodyMissing() throws {
stream.add(
input:
"""
-> mystanza
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.incompleteStanza)
}
}
func testReadFrom_BodyHasPadding() throws {
stream.add(
input:
"""
=> mystanza
IGVzdCBsYWJvcnVtLg==
""")
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.invalidStanza)
}
}
func testReadFrom_NoInput() throws {
XCTAssertThrowsError(try Stanza.readFrom(stream: stream)) { error in
XCTAssertEqual(error as! Plugin.Error, Plugin.Error.incompleteStanza)
}
}
func testWriteTo() throws {
Stanza(
type: "mytype",
args: ["MyArgument1", "MyArgument2"],
body:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
.data(using: .utf8)!
).writeTo(stream: stream)
XCTAssertEqual(
"""
-> mytype MyArgument1 MyArgument2
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np
bmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFi
b3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVu
aWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBu
aXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0
ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxp
dCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBF
eGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBz
dW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlk
IGVzdCBsYWJvcnVtLg
""", stream.output)
}
func testWriteTo_NoArguments() throws {
Stanza(
type: "mytype",
body: "Lorem ipsum".data(using: .utf8)!
).writeTo(stream: stream)
XCTAssertEqual(
"""
-> mytype
TG9yZW0gaXBzdW0
""", stream.output)
}
func testWriteTo_EmptyBody() throws {
Stanza(
type: "mytype",
args: [],
body: Data()
).writeTo(stream: stream)
XCTAssertEqual(
"""
-> mytype
""", stream.output)
}
}